- 投稿日:2020-08-07T23:58:43+09:00
【PHP入門】ログイン機能
はじめに
今回は、PHPとMySQLを使ってログイン機能を実装していきます。
※当ページは、【PHP入門】CRUD機能で作られたものを前提にしています。バージョン
PHP:7.4.5
phpMyAdmin:5.0.2
MySQL:5.7.30今回作成するファイル
html
- login.php
- login_process.php
- signup.php
- signup_process.php
- logout.phpmodel
- users.phpview
- login_view.php
- signup_view.phpテーブルの作成
bbs_user.sqlCREATE TABLE `bbs_users` ( `user_id` int(11) NOT NULL, `name` varchar(20) NOT NULL, `password` varchar(20) NOT NULL, `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `bbs_users` MODIFY `user_id` int(11) NOT NULL AUTO_INCREMENT, ADD PRIMARY KEY (`user_id`);定義
処理に使うものを追記
const.phpdefine('SIGNUP_URL', '/signup.php'); define('LOGIN_URL', '/login.php'); define('LOGOUT_URL', '/logout.php'); // 正規表現 define('REGEXP_ALPHANUMERIC', '/\A[0-9a-zA-Z]+\z/'); // 文字数制限 define('USER_NAME_LENGTH_MIN', 6); define('USER_NAME_LENGTH_MAX', 20); define('USER_PASSWORD_LENGTH_MIN', 6); define('USER_PASSWORD_LENGTH_MAX', 20);ユーザ登録
Modelの作成
users.phpを作成し、ユーザ登録・ログイン処理を記述
users.php// ログイン function get_user_by_name($db, $name){ $sql = " SELECT user_id, name, password FROM bbs_users WHERE name = ? "; return fetch_query($db, $sql, array($name)); } function login_as($db, $name, $password){ $user = get_user_by_name($db, $name); if($user === false || $user['password'] !== $password){ return false; } set_session('user_id', $user['user_id']); return $user; } // ユーザ登録 function regist_user($db, $name, $password) { if(is_valid_user($name, $password) === false){ return false; } return insert_user($db, $name, $password); } // バリデーション function is_valid_user($name, $password){ // 短絡評価を避けるため一旦代入。 $is_valid_user_name = is_valid_user_name($name); $is_valid_password = is_valid_password($password); return $is_valid_user_name && $is_valid_password ; } function is_valid_user_name($name) { $is_valid = true; if(is_valid_length($name, USER_NAME_LENGTH_MIN, USER_NAME_LENGTH_MAX) === false){ set_error('ユーザー名は'. USER_NAME_LENGTH_MIN . '文字以上、' . USER_NAME_LENGTH_MAX . '文字以内にしてください。'); $is_valid = false; } if(is_alphanumeric($name) === false){ set_error('ユーザー名は半角英数字で入力してください。'); $is_valid = false; } return $is_valid; } function is_valid_password($password){ $is_valid = true; if(is_valid_length($password, USER_PASSWORD_LENGTH_MIN, USER_PASSWORD_LENGTH_MAX) === false){ set_error('パスワードは'. USER_PASSWORD_LENGTH_MIN . '文字以上、' . USER_PASSWORD_LENGTH_MAX . '文字以内にしてください。'); $is_valid = false; } if(is_alphanumeric($password) === false){ set_error('パスワードは半角英数字で入力してください。'); $is_valid = false; } return $is_valid; } // ユーザデータの挿入 function insert_user($db, $name, $password){ $sql = " INSERT INTO bbs_users(name, password) VALUES (?,?); "; return execute_query($db, $sql, array($name, $password)); }functions.php// ログイン処理 function is_logined(){ return get_session('user_id') !== ''; } // バリデーション function is_valid_length($string, $minimum_length, $maximum_length = PHP_INT_MAX){ $length = mb_strlen($string); return ($minimum_length <= $length) && ($length <= $maximum_length); } function is_alphanumeric($string){ return is_valid_format($string, REGEXP_ALPHANUMERIC); } function is_valid_format($string, $format){ return preg_match($format, $string) === 1; }Viewの作成
※メッセージ表示のところは、繰り返し使用する為、別のファイルに記述し、それを読み込んでいます。
signup_view.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>ユーザ登録</title> </head> <body> <h1>ユーザ登録ページ</h1> <!-- メッセージ・エラーメッセージ --> <?php include VIEW_PATH. 'templates/messages.php'; ?> <!-- ログインフォーム --> <form method="post" action="signup_process.php"> <div> <label>ユーザ名:</label> <input type="text" name="name"> </div> <div> <label>パスワード:</label> <input type="password" name="password"> </div> <input type="submit" value="新規登録"> <input type="button" onclick="location.href='<?php print(LOGIN_URL); ?>'" value="ログインページへ"> </form> </body> </html>Controllerの作成
signup.php<?php require_once '../conf/const.php'; require_once MODEL_PATH. 'functions.php'; session_start(); // ログインされていれば、掲示板に遷移 if(is_logined() === true){ redirect_to(BBS_URL); } include_once VIEW_PATH. 'signup_view.php';signup_process.php<?php require_once '../conf/const.php'; require_once MODEL_PATH. 'functions.php'; require_once MODEL_PATH. 'users.php'; session_start(); // ログインされていれば、掲示板に遷移 if(is_logined() === true){ redirect_to(BBS_URL); } // Postされたものを定義 $name = get_post('name'); $password = get_post('password'); // データベース接続 $db = get_db_connect(); // ユーザ登録処理 try{ $result = regist_user($db, $name, $password); if($result === false){ set_error('ユーザ登録に失敗しました。'); redirect_to(SIGNUP_URL); } }catch(PDOException $e){ set_error('ユーザ登録に失敗しました。'); redirect_to(SIGNUP_URL); } set_message('ユーザ登録が完了しました。'); // ユーザ登録完了後、そのままログインして掲示板へ遷移 login_as($db, $name, $password); redirect_to(BBS_URL);ログイン
Viewの作成
※メッセージ表示のところは、繰り返し使用する為、別のファイルに記述し、それを読み込んでいます。
login_view.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>ログイン</title> </head> <body> <h1>ログインページ</h1> <!-- メッセージ・エラーメッセージ --> <?php include VIEW_PATH. 'templates/messages.php'; ?> <!-- ログインフォーム --> <form method="post" action="login_process.php"> <div> <label>ユーザ名</label> <input type="text" name="name"> </div> <div> <label>パスワード</label> <input type="password" name="password"> </div> <input type="submit" value="ログイン"> <input type="button" onclick="location.href='<?php print(SIGNUP_URL); ?>'" value="ユーザ登録ページへ"> </form> </body> </html>Controllerの作成
login.php<?php require_once '../conf/const.php'; require_once MODEL_PATH . 'functions.php'; session_start(); // ログインされていれば、掲示板に遷移 if(is_logined() === true){ redirect_to(BBS_URL); } include_once VIEW_PATH . 'login_view.php';login_process.php<?php require_once '../conf/const.php'; require_once MODEL_PATH. 'functions.php'; require_once MODEL_PATH. 'users.php'; session_start(); // ログインされていれば、掲示板に遷移 if(is_logined() === true){ redirect_to(BBS_URL); } // Postされたものを定義 $name = get_post('name'); $password = get_post('password'); // データベース接続 $db = get_db_connect(); // ログイン処理 $user login_as($db, $name, $password); if($user === false){ set_error('ログインに失敗しました。'); redirect_to(LOGIN_URL); } set_message('ログインしました。'); redirect_to(BBS_URL);ログアウト
Modelの作成
users.php// ログイン状況 function get_user($db, $user_id){ $sql = " SELECT user_id, name, password FROM bbs_users WHERE user_id = ? "; return fetch_query($db, $sql, array($user_id)); } function get_login_user($db){ $login_user_id = get_session('user_id'); return get_user($db, $login_user_id); }Viewの作成
bbs_view.php<p>ようこそ、<?php print($user['name']); ?>さん。</p> <a href="<?php print(LOGOUT_URL);?>">ログアウト</a>Controllerの作成
logout.php<?php require_once '../conf/const.php'; require_once MODEL_PATH. 'functions.php'; session_start(); $_SESSION = array(); $params = session_get_cookie_params(); setcookie(session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"] ); session_destroy(); redirect_to(LOGIN_URL);bbs.phprequire_once MODEL_PATH. 'users.php'; // ログインされてなければ、ログインページへ遷移 if(is_logined() === false){ redirect_to(LOGIN_URL); } $user = get_login_user($db);参考
- 投稿日:2020-08-07T21:30:10+09:00
AWSでLAMP環境を構築して、Laravelアプリをデプロイする
今回はAWSのEC2、RDSを用いてLAMP環境を構築して、Laravelアプリをデプロイする手順を紹介します。
今回の流れ
1.RDSを用いてDBサーバー作成
2.EC2を用いてWEBサーバーを作成
3.サーバー間の連携
4.アプリのデプロイ前提条件
・Macを使用
・AWSアカウントを作成済みであること
・GitHubのリモートリポジトリに、開発済みのLaravelアプリが置いてあること1.RDSを用いてDBサーバー作成
コンソールにサインインした後、AmazonRDSに移動します。
続いてオレンジ色のCreate databaseボタンをクリックします。
使用するデータベースエンジンを選びます。今回はMySQLを選択します。
バージョンはよしなに選択してください。
今回は無料利用枠のものを作成します。
本番環境の場合は適したプランを選択してください。
インスタンス名、マスターユーザー名、パスワードを設定します。
後ほど使いますので、安全な場所にメモしておきましょう。
インスタンスのスペック、ストレージの容量等を設定します。
今回はデフォルト設定で進めますが、本番環境の場合はよしなに選択してください。
ネットワークの設定を行います。
VPCを作成済みの方は作成したVPCを、そうでない方はデフォルトのVPCを設定しましょう。
最後にオプションの設定をします。
RDSインスタンス初期化時に自動で作られるデータベースの名前を設定します。
今後データベースに接続する際は、ここで設定したデータベース名を使用します。
オレンジ色のCreate databaseボタンをクリックして設定したデータベースを作成します。
2.EC2を用いてWEBサーバーを作成
コンソールからAmazon EC2に移動して、青色のLaunch Instanceボタンをクリックします。
使用するマシンのイメージを選択します。
今回はAmazon Linux 2 AMIを用います。
EC2インスタンスタイプを設定します。
CPU、メモリ、ストレージ、ネットワークパフォーマンス等を考慮してよしなに選択します。
今回はt2.microを選択します。(無料利用枠の対象です)
詳細の設定です。
VPCやサブネット等を設定できますが、今回はとりあえずVPCのみ設定しておきます。
RDSで設定したものと同じVPCを選択してください。
ストレージの設定をします。
他にインスタンスを作成されない場合は30GBまでは無料枠です。
今回はデフォルトの8GBで進めていきます。
続いてタグの設定ですが今回は未設定のまま進み、セキュリティグループの設定をします。
EC2インスタンスへのアクセス許可、制限を指定します。
まずはSSH接続の設定です。
Create a new security groupを選択し、新しいグループを作成します。
TypeのプルダウンからSSHを選択し、SourceのプルダウンからはMyIPを選択しましょう。
次にHTTP接続です。
Add Ruleをクリックした後、TypeのプルダウンからHTTPを選択し、SourceのプルダウンからはMyIPを選択しましょう。
今回は自分のIPからのみ接続できる設定にします。
本番環境で不特定多数のアクセスを集める場合はHTTPのSourceをAnywhereにしてください。
Review and Launchをクリックし確認画面に遷移し、設定内容に問題がなければLaunchをクリックしましょう。
最後に、SSH接続するためのキーペアを作成します。
Create new key pairを選択し、キーペアの名前を設定します。
設定後、Download Key Pairをクリックして.pemファイルをダウンロードします。
このファイルがなければSSH接続できないので、安全な場所で保管してください。
青色のボタンLaunch Instanceをクリックしてインスタンスを起動します。
3.サーバー間の連携
まずは先に作成したRDSの設定を変更し、EC2インスタンスからRDSインスタンスへのアクセスを可能にします。
AmazonRDSに移動します。
左側のダッシュボードのDatabasesをクリックして、先に作成したデータベースを選択します。
Connectivity & securityタブのセキュリティグループをクリックします。
デフォルトではEC2インスタンスからRDSインスタンスへのアクセスは許可されていません。
Inboundタブを開きeditをクリックしてセキュリティグループの編集を行います。
TypeをMySQL/Auroraに、SourceをCustomeに設定し先ほど作成したEC2のセキュリティグループの名前を入力します。
利用可能なセキュリティグループが表示されるので、先ほど作成したものを選択します。自動で値が入力されます。
青色のSavaボタンをクリックし設定を反映させます。
続いてEC2インスタンスにSSH接続していきます。
ターミナル上のキーペアがあるディレクトリで以下のコマンドを実行します。ssh -i .pemファイル名 ec2-user@パブリックIPパブリックIPは、接続するインスタンスのこちらをコピーペーストしましょう。
EC2インスタンスにSSH接続できたら、RDSに接続するための準備をしていきます。
MySQLをインストールしたいのですが、MariaDBがデフォルトでインストールされていると競合してしまうので、アンインストールしておきます。$ sudo yum remove mariadb-libsMySQLのリポジトリを追加して、MySQLをインストールします。
$ sudo yum localinstall https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm $ sudo yum install mysqlMySQLのホスト名を環境変数に設定して、データベースに接続します。
エンドポイントはRDSインスタンスのホスト名を入力します。
ユーザー名、パスワード、DB名は先に作成したものです。$ export MYSQL_HOST=エンドポイント $ mysql --user=ユーザー名 --password=パスワード DB名
Laravelアプリのためのユーザーを作成し、データベースへのアクセス権限を付与します。mysql> CREATE USER 'ユーザー名' IDENTIFIED BY 'パスワード'; mysql> GRANT ALL PRIVILEGES ON DB名.* TO ユーザー名; mysql> FLUSH PRIVILEGES; mysql> Exit次にEC2インスタンスにApacheとPHPをインストールします。
まずはApacheのインストールです。$ sudo yum install -y httpd $ sudo service httpd startパブリックIPアドレスにアクセスして、以下の画面が表示されていればインストール成功です。
PHP並びにLaravelアプリに必要な拡張モジュール等をインストールしていきます。
今回のLaravelのバージョンは5.5なので、対応したPHP拡張モジュールも合わせてインストールしておきます。
また、自分の開発したアプリに必要な拡張モジュールがあれば適宜追加でインストールしてください。$ sudo amazon-linux-extras install -y php7.2 $ sudo yum -y install php-openssl php-pdo php-mbstring php-tokenizer php-xmlこれでLAMP環境を構築することができました。
4.アプリのデプロイ
Laravelアプリのデプロイ、デプロイのための最終準備をしていきます。
まずはEC2インスタンスにGitをインストールします。$ sudo yum -y install git続いて/var/www/htmlに移動して、完成済みのLaravelアプリをgit cloneします。
$ cd /var/www/html $ sudo git clone URLApacheの設定を変更していきます。
/etc/httpd/confに移動して、httpd.confを編集します。$ cd /etc/httpd/conf $ vi httpd.conf以下が編集箇所です。
119行目付近でドキュメントルートを変更し、アクセスがあった際にアプリ直下のpublicディレクトリにファイルを探しにいってくれるようにします。
131行目付近、151行目付近でpublicディレクトリ内での設定の変更を有効にします。
最終行でmod_rewriteを許可することでURLの書き換えを可能にします。#119行目付近 DocumentRoot "/var/www/html/Laravelアプリ/public" #131行目付近 <Directory "/var/www/html/Laravelアプリ/public"> #151行目付近 AllowOverride All #最終行に追加 LoadModule rewrite_module modules/mod_rewrite.so変更後はApacheを再起動させます。
$ sudo systemctl restart httpd続いてComposerをインストールします。
/usr/local/bin/にインストールしてファイル名を変更することで、どこからでもcomposerコマンド一つで呼び出し可能にします。$ cd /usr/local/bin/ $ sudo php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" $ sudo php -r "if (hash_file('sha384', 'composer-setup.php') === 'e5325b19b381bfd88ce90a5ddb7823406b2a38cff6bb704b0acc289a09c8128d4a8ce2bbafcd1fcbdc38666422fe2806') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" Installer verified $ sudo php composer-setup.php $ sudo php -r "unlink('composer-setup.php');" $ sudo mv composer.phar composer最後の仕上げです。
アプリの環境変数の設定等を行っていきます。
まずはcomposer.lockファイルを基にパッケージをインストールします。
ディレクトリの権限も変更しておきます。こちらの変更はアプリ起動完了後によしなに変更しても良いかと思います。$ cd /var/www/html $ chmod 777 Laravelアプリ $ cd Laravelアプリ $ chmod 777 bootstrap/cache $ chmod 777 storage/framework/sessions $ chmod 777 storage/framework/views $ chmod 777 storage/logs vendor $ composer install.env.exampleファイルeを基に.envファイルを作成します。
$ cp .env.example .env $ vi .env先に作成したRDCインスタンスを基に、DB接続に関する変数を定義していきます。
ユーザー名、パスワードはRDCインスタンスに接続して登録したものを設定します。DB_CONNECTION=mysql DB_HOST=ホスト名(エンドポイント) DB_PORT=3306 DB_DATABASE=DB名 DB_USERNAME=ユーザー名 DB_PASSWORD=パスワードartisanコマンドを叩いてアプリケーションキーを作成します。
マイグレーションも実行しておきます。(開発済みのアプリなのでマイグレーションファイルがあることが前提です)php artisan key:generate php artisan migrateこれでパブリックIPアドレスにアクセスすれば、アプリケーションが動作しているはずです!
ここまでお疲れ様でした。
- 投稿日:2020-08-07T20:13:45+09:00
Laravelの基礎学習 with XAMPP
この記事の解説は独自の解釈が多いため間違っている可能性もあります。そのような箇所があれば指摘して頂けると幸いです。
目標
Laravelで簡易的なサイトが作れる程度の基礎を習得する
環境
- Windows 10 home
- XAMPP
- Laravel
Laravelの勉強
まずは初心者のためのLaravel入門 - libroを軸に勉強を進めていく。
学習の手順としては入れ替えた方が良い箇所もあるかもしれないが、自分にとって親しみやすい順で進めていく。Laravelのインストール
Composerのインストール
Laravel
のインストールにはComposer
というパッケージ管理ツールを使う。
PHP
で使用するソフトウェアやアプリケーションをインストールしたい時に、そのソフトウェアなどが動くのに必要な依存関係のある他のソフトウェアなども一緒にインストールしてくれる便利なツール。
Composer 公式からダウンロードして、PATH
を通して、ターミナルなどで次のような表示がされればOK。
Laravelインストーラーの導入
Laravel
では設定ファイルやコードなどを1つのプロジェクトとしてまとめて作業を行う。
ディレクトリやフォルダと同じ認識。では、
Laravel
のプロジェクトを作成していく。
今後もLaravel
プロジェクトを作成することを考えて、Composer
でLaravelインストーラーを導入してからLaravel
のプロジェクトを作成する。
Laravelインストーラーは下記のコマンドをターミナル実行して、インストールする。$ composer global require "laravel/installer"Laravelインストーラーのダウンロードが終わったら、赤下線部分をエクスプローラーで開く。
そこから~\vendor\bin
まで移動し、そこまでのパスをコピーする。C:\(各自のパス)\AppData\Roaming\Composer\vendor\binその後、
Windows10
の左下にある検索窓から「環境変数」を入力してシステム環境変数の編集
を開く
システムのプロパティ
が開けたら下の環境変数
(赤枠部)をクリック。
その後、下にある
システム環境変数(S)
からPath
を探してダブルクリック。
すると、環境変数名の編集
が開けるので、右上の新規
をクリックし、先程のパスを貼り付けて、OK
を押す。Laravelプロジェクトの作成
ターミナルに戻り、下記コマンドを実行。
$ laravel new blog(プロジェクト名)これで
Laravel
のプロジェクトが作成される。
今後、Laravel
のプロジェクトを作成したいときは、$ laravel new (プロジェクト名)
をターミナルで実行するだけで作成できるようになった。作成できたプロジェクトを実際に実行してみる。
ターミナルから以下のコマンドを実行。$ cd (プロジェクト名) $ php artisan serve
$ php artisan serve
でLaravel
の内蔵サーバーを起動できる。
つまり、このコマンドを利用することでローカルのアプリケーションとしてプロジェクトの状態確認が可能に。
ターミナルに表示された http://127.0.0.1:8000 にアクセスしてみると、Laravel
と中央に大きく書かれたサンプルページが開かれる。
ターミナルでCtrl + C
を押すとサーバーを閉じる。プロジェクト(フォルダ)構成について
プロジェクトを作成した際、一緒にインストールされたファイルなどはLaravelをスタートしよう(4/5):初心者のためのLaravel入門 - libroに解説が載っている。
ただ、この記事の情報は古いため、必要があれば下記コマンドでLaravel
のバージョンなどを確認し、公式ドキュメントなどで各自フォルダ構成について調査を。$ php artisan --versionルーティング処理
ルーティング処理: どのアドレスでアクセスされたら、どの処理を返すかを定義するもの
Laravel
では(プロジェクト名)\routes\web.php
でルーティング処理が指定されており、実際にファイルを確認してみると、以下のような宣言がある。(プロジェクト名)\routes\web.phpRoute::get('/', function () { return view('welcome'); });上記のコードを
Route::メソッド(第一引数, 第二引数)
として解説を行う。
Route
: クラス名。::
の後に続くメソッドを呼び出して処理を実行する。メソッド
:Route
クラスで定義されている処理から、どの処理を実行するかを指定する。ここによって次の第一引数
の値が変わる。第一引数
:メソッド
によって値が変わる。(例:get
メソッドではアドレスを表すテキスト。group
メソッドではアドレス情報をまとめた配列になる)第二引数
:第一引数
にアクセスされた際に返す処理。web.php
ではファイル名が指定されているがテキストなども返すことができる。以上の解説を踏まえて、現在の
web.php
を見てみると、/(ルート)
にアクセスされたらview(welcome)
を返すルーティング処理であることがわかる。
このルーティング処理で呼び出されるview(welcome)
がどこにあるか、どのファイルかという質問に対する答えは、(プロジェクト名)\resources\views\welcome.blade.php
。
view()
関数については次章で説明する。
welcome.blade.php
にblade
という見慣れない拡張子があるが、これはLaravel
で利用できるテンプレートの仕組み。
この仕組みを利用することで、blade
拡張子がついたファイル同士でコードを共有しあったり、Laravel
に搭載されている機能を簡単に呼び出したりすることができるようになる。ルーティング処理についてわかったところで、
Hello,World!!
を表示するようにweb.php
を変更してみる。
この後の作業で使用するため、変更前のコードをコメントアウトなどして残しておく。(プロジェクト名)\routes\web.phpRoute::get('/', function () { return 'Hello,World!!'; });表示用のファイル(テンプレート)について
web.php
で特定のURLにアクセスされた際の処理を定義できることが分かった所で、その結果を表示するページを作成していく。
今回は簡単なPHP
ファイルを作成するが、welcome.blade.php
のようなファイルを作成することもある。むしろ、そちらが主流。そのような.blade.php
拡張子ファイルはテンプレートと呼ばれ、この先で作成するため覚えておいてほしい。では、表示用のページとなるファイルから作成していく。
(プロジェクト名)\resources\views\
にtemplate.php
を作成し、下記の内容にする。(プロジェクト名)\resources\views\template.php<!DOCTYPE HTML> <html> <head> <title>template</title> <style> body{color:gray;} h1{font-size:18pt; font-weight:bold;} </style> </head> <body> <h1>Template</h1> <p><?php echo $message; ?></p> </body> </html>次に、
web.php
でこのファイルをを表示するルーティング処理を定義する。(プロジェクト名)\routes\web.phpRoute::get('/template', function () { return view('template',['message' => 'Study Template']); });ルーティング処理の内容は
/template
にアクセスされたらtemplate.php
を返すというものだが、view()
の中に連想配列が指定されている。ここで
ルーティング処理
の所で飛ばしたview()
関数の説明をする。
view()
関数はview(第一引数, 第二引数)
という形をとり、次のように宣言する。
第一引数
: ファイル名、テンプレート名を指定する。拡張子を除いた名前を宣言する。第二引数
:第一引数
に渡す値などを指定する。今回では連想配列を用いて、template.php
の変数$message
にStudy Template
という文字列を渡している。以上のファイル、ルーティング処理の作成によって表示されるページは次のようなものとなる。
http://127.0.0.1:8000/templateコントローラー
(プロジェクト名)\routes\web.php
にルーティング処理を定義することで、(プロジェクト名)\resources\views\
にある表示用のファイルやテンプレートを呼び出せるようになったがある問題が潜んでいる。
それはview()
の第二引数を利用する処理が増えると、web.php
のコードが複雑になることだ。
テンプレートの作成では第二引数に指定する連想配列のキーと値は1つだけだったが、web.php
で宣言するルーティング処理が増え、連想配列の数も増えれば見づらく、読みにくいコードとなってしまう。そのような問題を防ぐために使用されるのがコントローラーだ。
実際に作成しながら解説をしていく。コントローラーは
(プロジェクト名)\app\Http\Controllers
に作成する。
今回はその階層にTemplateController.php
というファイルを作成し、次の内容にする。(プロジェクト名)\app\Http\Controllers\TemplateController.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller; class TemplateController extends Controller { public function getIndex() { return view('template', ['message' => 'Study Template']); } } ?>そして、
web.php
のRoute::get('/template', ~
を下記のように変更する。(プロジェクト名)\routes\web.phpRoute::get('/template','TemplateController@getindex');
Route
クラスの第二引数に指定されているTemplateController@getindex
でTemplateController
のgetIndex()
メソッドを呼び出すことを宣言している。
TemplateController.php
のようなコントローラーの仕組みを説明をしていく。
新規作成したコントローラーのファイルではuse App\Http\Controllers\Controller;
でApp\Http\Controllers\Controller
というクラスを継承することを忘れずに宣言する。
public function getIndex()
で宣言されているgetIndex()
は、Route::get()
などのGET
アクセスでindex
というアドレスにアクセスした際に呼び出されるメソッドであることを定義している。
このTemplateController.php
自体がweb.php
のルーティング処理において、Route::get()
で/template
に割り当てられているため、/template/index(トップページ)
にアクセスすると、getIndex()
が呼び出され、view()
で定義した処理が実行される仕組みとなっている。コントローラーで指定するメソッド(
getIndex()
など)はHTTPメソッドの種類 + アクセスするアドレスによって名前が自動的に決まるため、このルールを覚えておく必要がある。http://127.0.0.1:8000/template にアクセスして、エラーが表示されていないか確認。
Requestクラス
ここでは
Request
クラスを利用して、フォームを使用したページ間でのデータの送受信ができるようにする。
この記事の軸にしていたサイトの情報が古かったので、ここではLaravel5 チュートリアル ブログもどきを作る(2) ブログ記事投稿フォームの作成 - Qiitaをメインに進めていく。まずはテンプレートの作成から。
GET
メソッド以外でフォームを利用する場合、Laravel
の仕様でCSRF(ログイン状態で悪意あるURLにアクセスするとログイン情報が盗まれ悪用される事)
の対策をしないとエラーが発生する。
その対策として活用できるのがLaravel
の機能であるblade
テンプレートだ。
(プロジェクト名)\resources\views\
にform.blade.php
を作成し、次の内容にする。(プロジェクト名)\resources\views\form.blade.php<!DOCTYPE HTML> <html> <head> <title>Form</title> <style> body{color:gray;} h1{font-size:18pt; font-weight:bold;} </style> </head> <body> <h1>フォームを利用したデータの送受信</h1> <p>{{ $message }}</p> <form method="POST" action="/form"> {{ csrf_field() }} <input type="text" name="str"> <input type="submit"> </form> </body> </html>
{{ }}
がblade
テンプレートの記法。
{{}}
自体は<?php echo ~ ?>
の代わりで、{{ csrf_field() }}
はこれだけでLaravel
におけるCSRF
対策の機能を呼び起こし、利用できる。続いて、コントローラーを作成する。
(プロジェクト名)\app\Http\Controllers
にFormController.php
を作成し、次の内容にする。(プロジェクト名)\app\Http\Controllers\FormController.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller; class FormController extends Controller { public function getIndex(Request $request) { return view('form', ['message' => 'データを入力してください。']); } public function postIndex(Request $request) { $res = "入力内容: ".$request -> input('str'); return view('form', ['message' => $res ]); } } ?>
postIndex()
はテンプレートの章でも説明したように、POST
アクセスでindex
に接続した際に実行される処理がそれ以降に記載されている。
そのため、POST
アクセスが行われていない何もデータが入力されていない時に表示する用のページとして、getIndex()
のメソッドも作成しておくことも必要。そして、今回の主題となるのが
Request
クラスを利用した(Request $request)
だ。
Request
クラスではユーザーからのアクセスを始めとする各種情報を管理している。
その内容を$request
に入れ、今回はinput('str')
という形でデータを取り出している。
input
に関するデータはinput(name属性)
で取り出せるため、このような記述となる。
(プロジェクト名)\routes\web.php
に次のルーティング処理を追記する。web.phpRoute::get('/form','FormController@getindex'); Route::post('/form','FormController@postindex');ここで意図した設計で稼働しているか確認してみる。
http://127.0.0.1:8000/form にアクセス。この状態で
送信
を押すと、このように変化する。
Requestクラスの基本(3/5):初心者のためのLaravel入門 - libro 以降ではクエリやURLの取得、リダイレクトについて記載されているため、確認してみては。
データベースとの連携
ここでは
XAMPP
のMySQL
を利用して、Laravel
とデータベースを連携させる。まずはデータベースの準備から。
XAMPP
を起動したら、XAMPP
のコントロールパネルにおけるMySQL
のAdmin
をクリックして、phpMyAdmin
に接続する。
phpMyAdmin
にログインできたら、データベース
タブから下記画像のように新規データベースを作成する。(画像ではデータベース名が頭文字が大文字だったが作成したら小文字になった。)
そして、新規テーブルを作成する。
好きな名前とカラム数を設定してテーブルを作成し、
ここまで(↓)設定できたら、次は
Laravel
側の設定に移る。
今回は
MySQL
を使用するが、他のデータベースを利用したい場合は、(プロジェクト名)\config\database.php
を開いて、下記の部分を使用したいデータベースに変える。(プロジェクト名)\config\database.php'default' => env('DB_CONNECTION', 'mysql'),どのデータベースを使用するか設定できたら、
(プロジェクト名)\.env
を開いて、下記にデータベース情報を入力していく。
デフォルトで値が入っているが、必要があれば変更する。(プロジェクト名)\.envDB_CONNECTION= DB_HOST= DB_PORT= DB_DATABASE= DB_USERNAME= DB_PASSWORD=ここからはデータベースに接続し、値を取得、ページに表示するまでを説明していく。
コントローラーの作成から行っていく。
(プロジェクト名)\app\Http\Controllers
にDatabaseController.php
を作成し、下記の内容にする。(プロジェクト名)\app\Http\Controllers\DatabaseController.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use DB; use App\Http\Requests; use App\Http\Controllers\Controller; class DatabaseController extends Controller { public function getIndex(Request $request) { $data = DB::select('select * from sample'); return view('database', ['message' => 'データベース名: sample','data' => $data]); } } ?>
use DB;
を忘れないように注意。
DB::select
はデータベースに対してSELECT
のクエリ文を実行するための関数。
select
以外にもDB
クラスにはデータベースを操作するためのメソッドがあり、それらをCRUD処理
と呼ぶ。
CRUD処理
とは、Create(生成)
,Read(読み取り)
,Update(更新)
,Delete(削除)
の処理における頭文字を並べたもの。
それぞれのDB
クラスにおけるメソッドはCreate(生成) = DB:insert()
,Read(読み取り) = DB:select()
,Update(更新) = DB:update()
,Delete(削除) = DB:delete()
となるので覚えておく。余談となるが、複数人がページやサイトにアクセスしていて、同時にデータベースに
CRUD処理
を実行した場合、Update
とDelete
が同時に実行する命令が起きるようなデータの不整合が起きる可能性がある。
そのような事態に陥るのを防ぐために行われるのがトランザクション
で、DB::beginTransaction();
からDB::commit();
の間に書かれた処理が実行されている間は他の命令が実行されることを防ぐことができるようになる。
本番環境でデータの書き換えを行うような処理を定義する場合は忘れずに記述するようにしておく。本題に戻って、コントローラーが取得したデータベースのデータを表示するページを作成していく。
(プロジェクト名)\resources\views\
にdatabase.blade.php
を作成し、下記のような内容に。(プロジェクト名)\resources\views\database.blade.php<!DOCTYPE HTML> <html> <head> <title>Form</title> <style> body{color:gray;} h1{font-size:18pt; font-weight:bold;} th{color:white; background:#999;} td{color:black; background:#eee; padding:5px 10px;} </style> </head> <body> <h1>Laravelとデータベースの連携</h1> <p>{{ $message; }}</p> <table> <tr> <th>ID</th> <th>NAME</th> <th>PASS</th> <th>CREATE_DAY</th> </tr> @foreach($data as $value) <tr> <td>{{ $value -> id; }}</td> <td>{{ $value -> name }}</td> <td>{{ $value -> pass }}</td> <td>{{ $value -> create_day }}</td> </tr> @endforeach </table> </body> </html>
@foreach($data as $value)
でコントローラーが取得したデータベースの各値が入っている$data
を$value
に移す。
{{ $value -> (カラム名) }}
で各カラムに入っているデータを取得して表示している。
(プロジェクト名)\routes\web.php
に今回のルーティング処理を実装して、実際にページを確認してみる。(プロジェクト名)\routes\web.phpRoute::get('/database','DatabaseController@getindex');自分の今までに掲載した画像の設定と同じ設定にすれば次のように表示されるはず。
http://127.0.0.1:8000/databaseORM(Object/Relational Mapping)について
さっきは自分でデータベースを作成して値を取得、表示まで行ったが、仕事でも同じような状況で作業ができるとは限らない。
違う人がデータベースを作成する事なんて普通だし、そもそもデータベースを見れずに手探りでデータを取得しなければならないことだってあり得る。
そのような状況で、今までのようなやり方では不都合が多い。そこで
PHP
のようなオブジェクト指向言語で用いられる技術がORM(Object/Relational Mapping)
だ。
この技術を用いることで、データベースのテーブルに存在しているデータ、値をPHP
のオブジェクトに変換したり、その逆の変換も行えるようになる。
その上、PHP
側で用意したクラスを操作するだけで、それに対応するデータベースのテーブルを自動的に操作できるようになるという。
Laravel
ではEloquent
というORM
が利用できるため早速使用してみる。最初にテーブルに対応するモデルと呼ばれるクラスの作成を行う。
ターミナルでプロジェクトのトップディレクトリに移動し、下記コマンドを実行。
自分はどのテーブルを扱うのかわかりやすいよう、テーブル: sample
に対し、モデル: Sample
を作成した。$ php artisan make:model Sample(モデル名)上記のコマンドが成功すると、
(プロジェクト名)\app
にSample(モデル名).php
が作成されているので、開いて確認してみると次のような内容になっている。(プロジェクト名)\app\Sample(モデル名).php<?php namespace App; use Illuminate\Database\Eloquent\Model; class Sample extends Model { // }
//
とコメントアウトにされている箇所があるが、そこをprotected $table = 'sample';
に変更して、使用するテーブル名を定義する。
protected
を付けることで、そのクラス自身と継承したクラス限定でアクセスが可能となるため、テーブルを保護するためにも欠かさずに付与する。
モデルの作成はこれで完了。コントローラーの作成に移る。
(プロジェクト名)\app\Http\Controllers
にORMController.php
を作成し、下記の内容を入力する。
データベースとの連携で使用した表示用のファイル(プロジェクト名)\resources\views\database.php
を使用するため、view()
関数の第一引数はdatabase
にする。(プロジェクト名)\app\Http\Controllers\ORMController.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Sample; use App\Http\Requests; use App\Http\Controllers\Controller; class ORMController extends Controller { public function getIndex(Request $request) { $data = Sample::all(); return view('database', ['message' => 'データベース名: sample','data' => $data]); } } ?>
use App\Sample;
で今回作成したモデルを使用することを宣言。
Sample::all()
では、Sample
クラスでモデル: Sample
に対応しているテーブル: sample
の全データをall
メソッドを使用して取得している。一回ここで、モデルなどが問題なく使えているか確かめる。
(プロジェクト名)\routes\web.php
にルーティング処理を実装し、実際にページを確認してみる。web.phpRoute::get('/orm','ORMController@getindex');http://127.0.0.1:8000/orm にアクセスしてみて、http://127.0.0.1:8000/database と同じ内容が表示されているか確かめる。
all
の他にもデータベースにおける処理に関するメソッドがモデルクラスには用意されている。
その中の一つが検索機能のwhere
だ。
where
はモデル名::where(カラム名, 取得したい値)
でテーブルに検索をかけて、get()
でその結果を取得する。
コントローラーに実装して、試してみるとそれぞれ次のようになる。ORMController.phpclass ORMController extends Controller { public function getIndex(Request $request) { $data = Sample::where('id', 2)->get(); return view('database', ['message' => 'データベース名: sample','data' => $data]); } } ?>検索を実行できる
where
には様々な使い方がある。
>
やlike
といった演算子を使いたい場合はwhere(カラム名, 演算子, 取得したい値)
、and
条件ならwhere(条件1) -> where(条件2)
、or
条件ならwhere(条件1) -> orWhere(条件2)
という風になる。
データベースから値を取得する機会は多いので、少なくともこれらだけでも覚えておきたい。Laravelからデータベースのデータを操作する
CRUD処理
において、Read
に関してはORM
のwhere
で触れたが、Create
とUpdate
,Delete
がまだなので、今回はそれらに触れていく。前準備
ORM
の際に作成したモデルに2つのコードを追加する。(プロジェクト名)\app\Sample(モデル名).phpclass Sample extends Model { protected $table = 'sample'; protected $guarded = array('id'); public $timestamps = false; }
protected
はテーブルだけでなく、データも保護できる。
protected $guarded = array('id');
で主キーを保護する。
更新や削除の際にprotected
で保護されていないと、連携しているテーブルにも影響を与えてしまう。
それらを防ぐためにもテーブルや主キーにはprotected
で忘れずに保護をかける。
Laravel
でデータベースにデータを作成、追加する際、create_at
,update_at
という、それぞれ作成日時と更新日時を示す項目が自動的に作成される。
今回利用するテーブルではこれらの項目を設けていないため、何もしない状態でデータベースにデータを作成、追加してしまうと、エラーの原因となる。
それを防ぐのがpublic $timestamps = false;
の部分。
このコードを追加することで、データベースにデータを作成、追加する際にcreate_at
,update_at
が生成されなくなる。
データベースにデータを作成、追加する処理をLaravel
に実装する際は、このコードを追加するかテーブルにcreate_at
,update_at
カラムの作成を忘れないように。Create
表示用のページとして
(プロジェクト名)\resources\views\
にcreate.blade.php
を作成し、次のような内容に。(プロジェクト名)\resources\views\create.blade.php<!DOCTYPE HTML> <html> <head> <title>CRUD</title> <style> body{color:gray; } h1{font-size:18pt; font-weight:bold;} th{color:white; background:#999;} td{color:black; background:#eee; padding:5px 10px;} </style> </head> <body> <h1>LaravelからのCRUD処理</h1> <p>{{ $message }}</p> <table> <tr> <th>ID</th> <th>NAME</th> <th>PASS</th> <th>CREATE_DAY</th> </tr> @foreach($data as $value) <tr> <td>{{ $value -> id }}</td> <td>{{ $value -> name }}</td> <td>{{ $value -> pass }}</td> <td>{{ $value -> create_day }}</td> </tr> @endforeach </table> <table> <form method="post" action="/create"> {{ csrf_field() }} <tr><td>NAME:</td><td><input type="text" name="name"></td></tr> <tr><td>PASS:</td><td><input type="text" name="pass"></td></tr> <tr><td>CREATE_DAY:</td><td><input type="text" name="day"></td></tr> <tr><td></td><td><input type="submit"></td></tr> </form> </table> </body> </html>大まかな内容としては、テーブルのデータを表示して、
post
で作成するデータを送信するというもの。
ID
に値する内容を入力するフォームを作成していないが、これはデータベースのAUTO_INCREMENT
を利用しているため。
AUTO_INCREMENT
とは他のデータが挿入された際に、適用しているカラムに自動でデータを入力してくれるデータベースの機能。
この機能を適用させるには、phpMyAdmin
を開いて、AUTO_INCREMENT
を適用したいカラムを選択して、変更
をクリック。
その後、
A_I
の下のチェックボックスにチェックを入れ、保存。
もし、使用しない場合は
ID
の値を入力するフォームを作成を忘れないように。
post
から投げられたデータはコントローラーでデータベースのテーブルに挿入する。
その処理は以下のような内容。(プロジェクト名)\app\Http\Controllers\CreateController.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use DB; use App\Sample; use App\Http\Requests; use App\Http\Controllers\Controller; class CreateController extends Controller { public function getIndex(Request $request) { $data = DB::select('select * from sample'); return view('create', ['message' => 'Create処理を実行する','data' => $data]); } public function postIndex(Request $request) { $name = $request -> input('name'); $pass = $request -> input('pass'); $day = $request -> input('day'); $data = array( 'name' => $name, 'pass' => $pass, 'create_day' => $day ); Sample::create($data); return redirect()->action('CreateController@getindex'); } } ?>データベースのテーブルにデータを作成する処理、
Create
ではモデル名::create()
メソッドを使用する。
getIndex()
はデータベースの連携の際の内容と同じなので説明を省略。
postIndex()
はpost
が実行されたときに行われる処理を示している。
input
に入力されたname
,pass
,day
を$data
という連想配列に格納して、モデル名::create($data)
で$data
の内容をデータベースのテーブルに格納。
その後、getIndex()
にリダイレクトするという流れになっている。最後に
(プロジェクト名)\routes\web.php
に下記のルーティング処理を実装し、実際にページを確認してみる。(プロジェクト名)\routes\web.phpRoute::get('/create','CreateController@getindex'); Route::post('/create','CreateController@postindex');
post
後(ID
の5,6が抜けてるのは作成中にエラーの対応をしていたため)
Update
表示用のページとして
(プロジェクト名)\resources\views\
にupdate.blade.php
を作成し、form
以下を次の内容に。
それ以外はCreate
で作成したcreate.blade.php
と同じ。(プロジェクト名)\resources\views\update.blade.php<form method="post" action="update"> {{ csrf_field() }} <tr> <td>ID:</td> <td> {{ optional($data)->id }} <input type="hidden" name="id" value="{{ optional($data)->id }}"> </td> </tr> <tr> <td>NAME:</td> <td><input type="text" name="name" value="{{ optional($data)->name }}"></td> </tr> <tr> <td>PASS:</td> <td><input type="text" name="pass" value="{{ optional($data)->pass }}"></td> </tr> <tr> <td>CREATE_DAY:</td> <td><input type="text" name="day" value="{{ optional($data)->create_day }}"></td> </tr> <tr> <td></td> <td><input type="submit"></td> </tr> </form>
id
以外の各カラムの更新内容を入力するフォームを作成。
optional()
は引数に指定したオブジェクトにアクセスしたり、そのプロパティを取得する 関数。
オブジェクトがnull
の時はnull
を返す。
post
から投げられたデータをデータベースに更新する処理を記述するコントローラーのclass
以下は次のような内容。
class
より上はCreate
で作成したcreate.blade.php
と同じ。(プロジェクト名)\app\Http\Controllers\UpdateController.phpclass UpdateController extends Controller { public function getIndex(Request $request) { $id = $request -> id; $table = DB::select('select * from sample'); $data = Sample::find($id); $msg = 'Update Sample Table [id = ' . $id . ']'; return view('update', ['message' => $msg,'Table' => $table,'data' => $data]); } public function postIndex(Request $request) { $id = $request -> input('id'); $data = Sample::find($id); $data -> name = $request -> input('name'); $data -> pass = $request -> input('pass'); $data -> create_day = $request -> input('day'); $data->save(); return redirect()->action('UpdateController@getindex'); } }
$id = $request -> id;
でURL
におけるクエリパラメータ(~/~?id=123
などの?
以降の部分)を取得している。
そこで取得した$id
をモデル名::find()
の引数に使用することで、引数に一致するレコードをデータベースから取得し、$data
などのインスタンスに格納できるようにしている。
データベースのテーブルにおけるデータを更新する際はsave()
メソッドを使用する。
(プロジェクト名)\routes\web.php
に下記のルーティング処理を実装し、実際にページを確認してみる。(プロジェクト名)\routes\web.phpRoute::get('/update','UpdateController@getindex'); Route::post('/update','UpdateController@postindex');
post
前: http://127.0.0.1:8000/update?id=7
フォームの内容を変更したい内容に変えてある。
Delete
表示用のページとして
(プロジェクト名)\resources\views\
にdelete.blade.php
を作成し、form
を次の内容に。
それ以外はCreate
で作成したcreate.blade.php
と同じ。(プロジェクト名)\resources\views\delete.blade.php<form method="post" action="update"> {{ csrf_field() }} <tr> <td>削除したいレコードのID:</td> <td> <input type="text" name="id" value="{{ optional($data)->id }}"> </td> </tr> <tr> <td></td> <td><input type="submit"></td> </tr> </form>削除したいレコードのIDを入力するフォームと送信ボタンだけでOK。
指定されたレコードをデータベースから削除する処理を記述する
DeleteController.php
は次のような内容。
class
より上はCreate
で作成したcreate.blade.php
と同じ。(プロジェクト名)\app\Http\Controllers\DeleteController.phpclass DeleteController extends Controller { public function getIndex(Request $request) { $data = DB::select('select * from sample'); return view('update', ['message' => 'レコードの削除','data' => $data]); } public function postIndex(Request $request) { $id = $request -> input('id'); $data = Sample::find($id); $data -> delete(); return redirect()->action('DeleteController@getindex'); } }
Create
,Update
で使用したコントローラーの内容がわかっていれば$data -> delete();
以外説明することもない。
$data -> delete();
もUpdate
で使用したsave()
メソッドと同じような使い方をして、そのデータを削除する使い方をするものだという覚え方で問題はない。いつも通り、
(プロジェクト名)\routes\web.php
に下記のルーティング処理を実装し、実際にページを確認してみる。(プロジェクト名)\routes\web.phpRoute::get('/delete','DeleteController@getindex'); Route::post('/delete','DeleteController@postindex');
post
前: http://127.0.0.1:8000/delete
番外編: バリデーション
CRUD処理
の番外編としてバリデーションにも触れていく。
この機能は入力内容をチェックし、設定した条件を満たしているかどうかを判断するもので実装する方法は複数あるが、今回はフォームのバリデーションを実装する。
フォームのバリデーションはLaravel
において、フォームリクエストと呼ばれる。
今回はUpdate
で使用したファイル達に、このフォームリクエストを適用していく。フォームリクエストは以下のコマンドをターミナルで実行して作成する。
$ php artisan make:request UpdateRequest(作成したいファイル名)処理が完了すると
(プロジェクト名)\app\Http\Requests
にUpdateRequest(作成したいファイル名).php
が作成される。
UpdateRequest(作成したいファイル名).php
の各メソッドを次のように変更する。(プロジェクト名)\app\Http\Requests\UpdateRequest(作成したいファイル名).phppublic function authorize() { return true; } public function rules() { return [ 'name' => 'required|string|max:10', 'pass' => 'required', 'day' => 'required|string|max:10', ]; }バリデーションする条件の指定は、このように連想配列の形をとる。
どのような条件が指定できるかは公式ドキュメントに記載されているため、必要があれば確認を。そしたら、
(プロジェクト名)\app\Http\Controllers\UpdateControllers.php
に作成したバリデーションを適用させていく。(プロジェクト名)\app\Http\Controllers\UpdateControllers.phpuse App\Http\Requests; use App\Http\Requests\UpdateRequest; // 中略 public function postIndex(UpdateRequest $request) {変更といっても、
use
の箇所にuse App\Http\Requests\UpdateRequest;
を追加、public function postIndex(Request $request)
をpublic function postIndex(UpdateRequest $request)
に変えるだけ。これで http://127.0.0.1:8000/update?id=4 にアクセスして、フォームの中を全て空にして
送信
を押すと、最初の状態に戻り、バリデーションが効いていることがわかる。ただ、エラーメッセージもなしに画面が遷移するのはわかりにくいので、表示用のページである
(プロジェクト名)\resources\views\update.blade.php
も変更する。(プロジェクト名)\resources\views\update.blade.php// 略 .alert{color:red;} </style> </head> // 中略 </table> @if ($errors->any()) <div class="alert"> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif </body>これでバリデーションが適用された際、どのフォームが条件を満たしていないかが赤字で表示されるようになった。
Laravel
からテーブルを操作するここまでで
Laravel
側からデータベースのテーブルに対する操作ができることを学んだが、Laravel
ではテーブルそのものを作成することもできる。ここからはLaravel入門 - 使い方チュートリアル - - QiitaとLaravel5 チュートリアル ブログもどきを作る(1) DB設定・マイグレーション - Qiitaシリーズを軸に進めていく。
マイグレーション
Laravel
のテーブルそのものを作成する仕組み、それがマイグレーション
だ。まずはデータベースにテーブルを作成するためのマイグレーションファイルを作成するところから始まる。
今回はlaravels
というテーブル名で、それを作成するためのマイグレーションファイルを以下のコマンドをターミナルに入力して作成する。
テーブル名の最後にs
を忘れないように。$ php artisan make:migration laravels(作成したいマイグレーションファイル名) --create=laravels(作成したいテーブル名)そうすると、
(プロジェクト名)\database\migrations
に2020_08_06_095118_laravels(作成したいマイグレーションファイル名).php
が作成される。(プロジェクト名)\database\migrations\2020_08_06_095118_laravels(作成したいマイグレーションファイル名).php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class Laravels extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('laravels', function (Blueprint $table) { $table->id(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('laravels'); } }
public function up()
にはテーブルを作成するときの処理が、public function down()
にはテーブルを削除する処理が記載されている。
このまま、このファイルを実行してもテーブルを作成してもよいが、次のように少しカラムの設定を宣言してみる。(プロジェクト名)\database\migrations\2020_08_06_095118_laravels(作成したいマイグレーションファイル名).phppublic function up() { Schema::create('laravels', function (Blueprint $table) { $table->increments('id'); $table->string('name', 10); $table->timestamps(); }); }この状態で
migration
が実行されると、次の設定が反映されたカラムになる。
カラム名 設定 id AUTO_INCREMENT name VARCHAR(10) create_at timestamp update_at timestamp 上でも説明したように、
create_at
とupdate_at
のカラムは自動で作成され、AUTO_INCREMENT
は自動挿入、timestamp
は日時が自動で挿入される機能。では、このファイルを実行して、テーブルを作成してみる。
ターミナルに次のコマンドを入力することで、マイグレーションファイルが実行される。
$ php artisan migrate
migrate
でテーブルを作成するpublic function up()
の処理が行われた。
public function down()
の処理はmigrate:rollback
やmigrate:reset
で実行する。
migrate:rollback
は 最後に行ったマイグレーション操作を取り消す。
migrate:reset
は 全てのマイグレーションを削除する。シーディング
シーディング
はテーブルの初期化やテストデータとなるレコードを挿入するLaravel
の仕組み。
今回はこの仕組みを利用して、マイグレーション
で作成したmigration
テーブルを初期化(最初のデータを挿入)する。
シーディング
もマイグレーション
同様、ファイルの作成から始めるが、その前にマイグレーションで作成したテーブルに対応するモデルの作成を行う。$ php artisan make:model laravel(テーブル名の最後のs抜き)マイグレーションの時のテーブル名における最後の文字に
s
を付けてもらった理由がここにある。
モデル名はテーブル名の単数形にすることで自動的にテーブルを判断する機能がある。
テーブル名: 複数形
、モデル名: 単数形
の命名規則を忘れないように。ファイルを編集する必要はないので
(プロジェクト名)\app\
にlaravel(テーブル名の最後のs抜き).php
があるのを確認すればOK。その後、ターミナルで下記のコマンドを実行。
$ php artisan make:seeder StudySeeder(作成したいシーディングファイル名)そうすると、
(プロジェクト名)\database\seeds
にStudySeeder(作成したいシーディングファイル名).php
が作成される。(プロジェクト名)\database\seeds\StudySeeder(作成したいシーディングファイル名).php<?php use Illuminate\Database\Seeder; class StudySeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { // } }
migration
テーブルに挿入するデータは下の表。
id name create_at update_at 1 Laravel 作成日時 作成日時 2 Migration 作成日時 作成日時 3 Seeding 作成日時 作成日時
StudySeeder(作成したいシーディングファイル名).php
で、この表の内容を入力できるようにするには次の内容にする。(プロジェクト名)\database\seeds\StudySeeder(作成したいシーディングファイル名).phppublic function run() { DB::table('laravels')->truncate(); $table_data = [ ['name' => 'Laravel'], ['name' => 'Migration'], ['name' => 'Seeding'] ]; foreach($table_data as $data) { \App\laravel::create($data); } }
DB::table('laravels')->truncate();
でシーディングの対象となるテーブルの指定と初期化。
$migrationdata~
には連想配列の形式で入力したいレコードのデータを挿入。id
,create_at
,update_at
は自動挿入されるようにテーブル側で設定されているため、name
カラムだけ指定。
foreach($table_data as $data)~
で挿入するデータを一つずつテーブルに挿入していく。
\App\laravel::create();
のlaravel
部分は各自のモデル名に。シーディングではコマンド実行時に同じディレクトリにある
DatabaseSeeder.php
を実行するため、このファイルも編集する。(プロジェクト名)\database\seeds\DatabaseSeeder.phppublic function run() { $this->call(StudySeeder::class); }
StudySeeder
の部分を各自のシーディングファイル名に変更。以上で設定は終わりなのでシーディングを実行してみる。
シーディングの実行はターミナルで次のコマンドを叩く。$ php artisan db:seedLaravelシステム作成手順
今回の学習を踏まえて
Laravel
の作成手順をまとめる。0.
(プロジェクト名)\.env
にデータベース設定を記述
1. マイグレーションでテーブルを作成
2. シーディングでテーブルにデータ挿入
3.(プロジェクト名)\resources\views\
に表示用のファイル、blade
テンプレートを使用したファイルを作成
4.(プロジェクト名)\app\Http\Controllers\
に3で作成したファイルへデータなどを渡すコントローラーを作成
5. バリデーション作成
6.(プロジェクト名)\routes\web.php
でルーティング処理おわりに
今回は
Laravel
で簡易的なサイトが作れるようになるまでの基礎を学んだ。
Laravel
にはもっと多くの機能があり、この記事では触れられていないことが多い。
下記の参考資料などを参考に学習を進めていき、自分が作成したいサイトやシステムをLaravel
で実装できるようにしていく。参考資料
- 初心者のためのLaravel入門 - libro
- Laravel - Laravelで「"Attribute [controller] does not exist."」というエラーに困ってます。|teratail
- Laravel5 チュートリアル ブログもどきを作る(2) ブログ記事投稿フォームの作成 - Qiita
- PHPのアクセス修飾子public, protected, privateの違い | UX MILK
- 【Laravel】Trying to get property フィールド名 of non-objectについて – ネコと征く戦場
- 【Laravel5.8】FormRequestを使うとThis action is unauthorized.が吐き出される - Laravelとねころっけくん5.8
- Laravel入門 - 使い方チュートリアル - - Qiita
- Laravel5 チュートリアル ブログもどきを作る(1) DB設定・マイグレーション - Qiitaシリーズ
- 投稿日:2020-08-07T18:43:30+09:00
FuelPHPでSQLの最新のレコードを取得する
SQLにあるレコードの、
created_at
が最も新しいものを取得したいといったとき、
SELECTにMAX(created_at)
とするだけでやってくれます。FuelPHPの場合は
example.php$query = \DB::select('id', \DB::expr('MAX(created_at)')) ->from('table_name') ->execute();とするだけでできます。
これに加えて
group_by('id')
などを使うときにすごく役立ちます。配列で取得したあとに頑張って新しい配列に入れるみたいなことをせずに済みます。(自戒)
- 投稿日:2020-08-07T16:12:27+09:00
使用してきた関数(メソッド)を忘れないように記載していくよ(PHP版 随時更新)
はじめに
今まで使用してきたメソッド
これから使用してみるメソッド
忘れないように、随時更新していきます。時間の都合上、
コードは載せる場合と、
載せない場合があります。例)
▶︎ メソッド意味:
参考コード
雛形
一つ一つ書くのが手間なので、
雛形を書かせていただきました!書いていきます٩( ᐛ )و
▶︎ is_null
意味:nullであればtrue,それ以外であればfalseを返す
参考コード
<?php $value = NULL; if (is_null($value)){ echo 'NULLです。'; }else{ echo 'NULLではありません。'; } 実行結果 //NULLです。 ?>▶︎ asset
意味:「publicディレクトリ」のパスを返すヘルパ関数
ヘルパとはviewファイルで使えるメソッドのことです。
現在のURLのスキーマ(httpかhttps)を使い、
アセットへのURLを生成するメソッド参考コード
<img src="{{ asset('storage/image/' . $headline->image_path) }}"> //保存した画像のファイル名が $head->image_pathに入っていることを想定 //このことで「.」を使用して、画像のパスを返していることになります。
- 投稿日:2020-08-07T15:19:59+09:00
Laravelの学習備忘録 その3
プログラマー初心者です。
Laravelを触って頑張っています。
Laravelでアプリを作りながら学んだことを雑多に記していきます。
記事の正確性は低めです。なので、各項目ごとの参考を参照したほうが良いと思います。
それでは!バリデーション
参考:ドキュメント「バリデーション」
Laravelではバリデーションの方法がいくつかあるようだ。
- validate()メソッドを使う
- FormRequestを使う←おすすめとのこと。FormRequestを使う方法は、学習済み。
validateメソッドの方法でも試してみよう。
やりかたは、コントローラの中で、$requestを受けるときに、
$validated_date = $request->validate([バリデーションルールを記述]);
でオッケー。簡単。でも、FormRequestを使おう。なぜなら、コントローラーをすっきりさせて、見通しを良くしたいから。
また、より複雑なロジックを組める。$fillableか$guardedか
参考:Qiita「僕がLaravelのEloquentに$fillableでなく$guardedを指定する理由」
\$fillableで指定したもののみ代入可能(ホワイトリスト化)。
\$guardedは指定したものが代入不可能(ブラックリスト化)。
記事の内容が僕にはまだ理解しづらかったので、とりあえず今はfillableをつかう。中間テーブル
参考:Qiita「中間テーブルとは」
- 多対多のテーブル同士のリレーションだと、お互い関係させようとすると、カラムが無駄に多くなったり、空白になってしまう。
- 例:articlesテーブルとtagsテーブルがあるときに、例えば、articlesタグにtagカラムをつけようとすると、1つの記事にタグをつけたいけど、いくつつくかわからないし、tagカラムがいっぱい作っておかないといけないし、空白フィールドもできて管理しづらくなる。
そこで間に1つテーブル入れることで、空白のカラムをつくることなく、管理しやすくなる。詳しくは上の参考を見るのが一番いい。
- なお、laravelで中間テーブルのマイグレーションを作るときは、単数形で!マイグレーションでのつまづき
複数形
テーブル、マイグレーション作成時に複数形にするとLaravelではきちんと関係性を読み込んでくれるのだが、複数形にするときに困ったのが、'diary'の複数形。
'diarys' or 'diaries' どっちだ。答えはdiaries
'diarys'で最初作ったら、マイグレートのときにエラーが出て先に進めなかった。やり直して、diariesでいったら、通った。単数形にsをつけるだけじゃないんだね。英語の初歩って感じだけど、複数形の法則ってちゃんとデータベースに仕組みが入っているのね。
マイグレーションファイルの作成順番
外部キー制約させたい元のテーブルのマイグレーションファイルは先に作っておかないと、エラーがでるので、マイグレーションの順番にもちょっと注意が必要。
よく考えてみれば、日付が新しい順から実行してマイグレーションupするんだから、「外部キーがないよ」ってなるのは当たり前か。
一気にマイグレーションファイルを作ってからマイグレートでいいけど、ファイルの作成順番が大事ということで。
- 投稿日:2020-08-07T15:05:53+09:00
[個人メモ][PHP]ドメインのSSL証明書の有効期限の取得(subjectAltNameまで見る)
個人メモとして記事っぽくまとめ。
PHPでドメインのSSL証明書の有効期限を取得するコードは先人たちの英知に学ぶとして大幅に前段を省略しますが、最後はopenssl_x509_parseで返ってきた値の[subject][CN]と対象ドメインを比較して有効期限の取得判断をしています。
この時面倒なのが、例えばwww.yahoo.co.jpでこれを調べてみると、のように*付きでくるので単純比較では不一致になってしまうことですね。
まぁこれは正規表現でも使ってあげればいいので、脳死コーディングするならif( strpos($parsed['subject']['CN'], $domain_name) !== false || preg_match(sprintf('/^.%s$/',$parsed['subject']['CN']), $domain_name) === 1 ) { echo '有効期限:' . date('Y/m/d', $parsed['validTo_time_t']); }とでもしてあげればよいのですが、面倒なものだと、
とかあるわけですね。
そんなときは、[extensions]の[subjectAltName]まで見てあげないといけないはずです。うわっめんどい、と一瞬思いますが、単純なパース処理なので、難しく考えずexplode、foreach、preg_matchあたりを使えばできるかと。余計な空白もあるのでtrimも忘れずに。
まぁここまで厳密に有効期限チェックするような監視よろしくなバリバリの仕組みでなければ、せいぜい*あたりだけ見てあげればいい気はしますね。知らんけど。
とここで終わるのもなんなので、
$arrDomain = [ 'qiita.com', 'www.youtube.com', 'www.google.co.jp', 'www.yahoo.co.jp', ]; foreach($arrDomain as $domain_name) { echo '■' . $domain_name . PHP_EOL; $stream_context = stream_context_create(array( 'ssl' => array('capture_peer_cert' => true) )); $resource = stream_socket_client( 'ssl://' . $domain_name . ':443', $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $stream_context ); $cont = stream_context_get_params($resource); $parsed = openssl_x509_parse($cont['options']['ssl']['peer_certificate']); $func_chk_domain = function($cn, $domain) { return (strpos($cn, $domain) !== false) || (preg_match(sprintf('/^.%s$/',$cn), $domain) === 1); }; // 単純にCNで比較(完全一致と*時の正規表現) $check_flg = false; if( $func_chk_domain($parsed['subject']['CN'], $domain_name) ) { echo 'CNで一致' . PHP_EOL; $check_flg = true; } else { // CNで一致しない場合はextensionsのsubjectAltNameを精査 $arrDNS = explode(',', $parsed['extensions']['subjectAltName']); foreach($arrDNS as $dns) { if( $func_chk_domain(str_ireplace(['DNS:',' '],'',$dns), $domain_name) ) { echo 'subjectAltNameで一致' . PHP_EOL; $check_flg = true; break; } } } if( $check_flg ) { echo '有効期限:' . date('Y/m/d', $parsed['validTo_time_t']) . PHP_EOL; } else { echo '取得NG' . PHP_EOL; } }で取れました。
- 投稿日:2020-08-07T11:35:04+09:00
MediaWikiのAPIを使ってページを自動作成する
筆者は、MediaWikiを自分のための情報収集と整理に使っています。いわば、個人のナレッジマネジメントですが、そのときに重宝しているのが、MediaWikiのRESTfulなAPIを使用するクライアントアプリケーションです。タイトルとページコンテンツを与えて新規ページを作成するという至って簡単なものですが、その割には役にたっています。記述言語はPHPです。
最近ではAPIを使ったCMS管理が必須になっていますが、APIを使ったクライアントアプリケーションの作成は簡単だよ、という例です。
ソースはGitHubにあげています。MediaWikiシステムをプライベートに利用している方は(あまり多くないとは思いますが)、カスタマイズしてそれぞれの用途で利用してください。
週別のテンプレートとは
筆者が最も利用しているのが、図1ようなテンプレートです。これは2020年29週の分ですが、週毎の月日と曜日、リンクをつけただけの簡単なものです。
リスト1「2020年29週」[[週報:2020年28週|前週]] --- [[週報:2020年30週|次週]] == 7/13(月) == == 7/14(火) == == 7/15(水) == == 7/16(木) == == 7/17(金) == == 7/18(土) == == 7/19(日) == <hr /> [[週報:2020年28週|前週]] --- [[週報:2020年30週|次週]]リスト1のテンプレートは、実際には図1のように表示されます(月曜日の欄には既にリンクメモが付加されています)。こんな具合に、思いついたメモや当日得た情報をその日の欄に順不動で追加して行きます。
MediaWikiの機能として上部に目次が追加されます。各小見出しにはid属性が付与されますので、外部からリンクすることが可能です。この辺りの基本機能はQiitaとほとんど同じです。
「編集」という青のリンクは、見出し以下の編集機能です。CMSの仲間であるQiitaと違って、各小見出し以下だけを(次の同レベル以上の小見出しまで1)編集できるのが便利です。Qiitaは全文を編集しなければならないので長い記事は書きにくいのですが、MediaWikiは適当に小見出しを入れることにより編集画面を小さくできます。
最初は、リスト1を手書きで追加していたのですが、日付や曜日を調べるのが意外に面倒です。週番号を間違えたりすると、ややこしいことになってしまいます。そこで、テンプレートを自動作成することにしました。
必要条件
以下のアプリケーションを実行するためには
- MediaWikiのデフォルト認証を使っている(PluggableAuthを使用していない)
- 使用するユーザIDにページ作成権限がある
ことが必要です。PluggableAuthによる認証を使用している場合は、OAuth認証が必要になります。MediaWikiのAPIにおけるOAuth認証については、別記事にする予定です。
MediaWikiのAPI
MeidaWikiのAPIのドキュメントは、公式サイトのAPI:Mainページ以下にあります2。コマンド名に相当するactionプロパティだけで120種類を越える膨大なAPI群です。但し、ここで使うactionは、
- query
- edit
- clientlogin
の3種類だけですので、とても簡単です。
処理の流れ
全体的な処理の流れは図2のようになります。PHPの起動スクリプトはローカルサーバ上に置きます。ページがなければ自動的に作成します。最後にリダイレクトしますので、このURL(図2ではlocalhostの8081番ポートにアクセスしています)にアクセスすれば常にその週のページが表示されるので便利です。筆者はブラウザのホーム画面にしています。
PHPの起動スクリプトをリスト2に示します(例外処理等は省略しています。詳しくはGitHubをみてください)。経験のある方なら、クラスの名前だけで何をしているかおわかりになるでしょう。
リスト2011: // パラメータを得る 012: $config = require_once 'config/ClientConfig.php'; 013: // get()とpost()の実装 014: $client = new MwCurlClient($config); 015: // RESTfulなAPIに対するエージェント 016: $agent = new MwAgent($client); 017: // ログイン 018: $agent->clientLogin( 019: $config['username'], 020: $config['password'], 021: $config['url'] 022: ); 023: // $diff=0は今週をあらわす 024: $diff = isset($_GET['diff'])?$_GET['diff']:0; 025: // タイトルは「20XX年:YY週」,YY=今週の週番号 026: $title = MwWeeklyReport::getTitle($diff); 027: if ($agent->existsPage($title) == false) { 028: // ページが無いなら、あらかじめ作成する 029: // 週番号に対するテンプレートを得る 030: $text = MwWeeklyReport::getBlankPage($diff); 031: $agent->createNewPage($title, $text); 032: } 033: // 「20XX年:YY週」のページにリダイレクト 034: header('Location: ' . $agent->getUrl($config['url'], $title));リスト2に登場するオブジェクトを次にあげます。
- ClientConfig: usernameなどのパラメータを格納する配列
- MwCurlClient: PHPのcurl関数群を使用してget()とpost()を実装する
- MwAgent: MediaWikiのAPIにアクセスする各種のメソッド
- MwWeeklyReport: リスト1でコンテンツマネージャと記したクラス。ページタイトルとテンプレートを作成する
以下では、MwAgentクラスの関連するメソッドについて説明します。
clientLogin()
MwAgentクラスのclientLoginメソッドをリスト3に示します。
「action=clientlogin」に必要なpostパラメータについてはclientloginの解説を参照してください。パラメータのうち「logintoken」は、事前に取得しておく必要があります3。
実際には、ここで指定したパラメータに加えて「format=json」を指定する必要がありますが4、post()の実装のなかで付加していますので、リスト3で指定していません。
リスト3101: public function clientLogin($user, $password, $rootUrl) { 102: $token = $this->getLoginToken(); 103: $result = $this->client->post(array( 104: 'action' => 'clientlogin', 105: 'loginreturnurl' => $rootUrl, 106: 'username' => $user, 107: 'password' => $password, 108: 'logintoken' => $token 109: )); 110: $status = $result['clientlogin']['status']; 111: if ($status !== 'PASS') { 112: throw new Exception("'clientlogin' failed. STATUS = " . $status); 113: }; 114: }getLoginToken()
clientloginに必要なログイン用トークンはリスト4のように取得します。「action=query」はデータを取得するためのAPIです。指定するパラメータにより様々なデータが取得できます。「meta=tokens」は、セキュリティー上の理由で必要なトークンを取得するのため使用します。
clientLogin()と同様、ここで指定したパラメータに加えて「format=json」を指定する必要がありますが4、get()の実装のなかで付加していますので、リスト4で指定していません。以下も同様です。
リスト4201: protected function getLoginToken() { 202: $result = $this->client->get(array( 203: 'action' => 'query', 204: 'meta' => 'tokens', 205: 'type' => 'login' 206: )); 207: return $result['query']['tokens']['logintoken']; 208: }existsPage()
指定したタイトルを持つページが存在するかどうかを調べるだけのものです。これも「action=query」を使う問い合わせです。
リスト5301: public function existsPage($title) { 302: $result = $this->client->get(array( 303: 'action' => 'query', 304: 'titles' => $title 305: )); 306: $pages = $result['query']['pages']; 307: $id = array_keys($pages)[0]; 308: return $id >= 0; 309: }createNewPage()
「action=edit」を用いた操作です。
コンテンツマネージャ(MwWeeklyReport)から得たテンプレートを内容として、新たなページを作成します。ここではページ作成と呼んでいますが、編集の場合も全く同じ手順です(と言っても、編集操作はMediaWiki画面で行うのが通常です)。getCsrfToken()は、getLoginToken()から「type=login」を除いただけですので、省略します。
これまでの例では、get()でもpost()でも良かったのですが5、「action=edit」だけはpost()を使う必要があります。
リスト6401: public function createNewPage($title, $text) { 402: $token = $this->getCsrfToken(); 403: $result = $this->client->post(array( 404: 'action' => 'edit', 405: 'token' => $token, 406: 'title' => $title, 407: 'text' => $text 408: )); 409: $status = $result['edit']['result']; 410: if ($status != 'Success') { 411: // 作成に失敗した 412: throw new Exception("Failed to create new page."); 413: } 414: }あとがき
APIを使ってCMSにアクセスすることが簡単だと思ってもらえたでしょうか。もし、そうでないとしたら私の説明不足ですので、気軽に質問してください。
なお、ローカルPCの起動スクリプトをサービスとして起動する場合の処理については、「PHPのビルトインウェブサーバをsystemdのサービスとして登録する」という記事にしました。ご参考に。
MediaWikiの「Wiki markup言語」では、見出しを複数の「=」ではさみます。「=」の数が見出しのレベルで、2個の「=」で挟むとhtmlのh2に変換されます。Qiitaで使う「Markdown言語」では前置きの「#」に相当します。 ↩
残念ながら、日本語ページはあまり内容が伴っていないので、なるべく英語ページを参照してください ↩
あらかじめトークンを取得する必要があるのは、XSS対策のひとつです ↩
最近では、結果をjsonで戻すことは、RESTfulなAPIにおける事実上の標準になっています。MediaWikiでも、「format=json」がデフォルト設定であるとドキュメントに書かれているのですが、v1.34.2では、まだそうはなっていないようです ↩
clientLogin()でpost()を使っているのは、セキュリティー上の理由です ↩
- 投稿日:2020-08-07T10:51:04+09:00
Laravelのタスクスケジュールが動かない(24時設定)
昨日まで動いていたのに、今日は動かない・・・
昨日のリリースが悪いのかしら。。でも何も悪いことしていないのに・・・スケジュール定義を見直そう
スケジュールタスクは全部
App\Console\Kernel
クラスのschedule
メソッドの中に定義します。
この定義でミスしていました。
毎日24時に実行したいからって$schedule->command('cms:update-article-point')->dailyAt('24:00');と書くと落ちます。こんなログを吐いて死にます。
In CronExpression.php line 155: Invalid CRON field value 24 at position 1このように書きましょう。
$schedule->command('cms:update-article-point')->dailyAt('00:00');
- 投稿日:2020-08-07T10:32:19+09:00
[PHP] 簡易ログイン機能を実装してみた
はじめに
phpを使用して、簡易的なログイン認証を実装してみました。(技術力低いのでやばいかもしれないが...)
基本的な流れ
1. idとpasswordを入力する。
2. 入力したidとpasswordがデータベースに紐づけされて登録されているかをチェック。
3. 登録されていたらログインし、以外はエラーを表示する。まぁ簡易的なのでそんなかんじです笑笑
該当ソースコード
1. login.php
ログインするために、ID、パスワードを入力する画面です。
ログイン画面(login.php)<?php require_once("function.php"); session_start(); header("Content-type: text/html; charset=utf-8"); ?> <!doctype html> <html lang="ja"> <head> <link rel="stylesheet" type="text/css" href="style1.css"> </head> <div class="form-wrapper"> <h1>ログイン</h1> <?php if (isset($_SESSION["error_status"])) { if ($_SESSION["error_status"] == 1) { echo "<h2 style='color:red'>IDまたはパスワードが異なります。</h2>"; } if ($_SESSION["error_status"] == 2) { echo "<h2 style='color:red'>不正なリクエストです。</h2>"; } //エラー情報のリセット $_SESSION["error_status"] = 0; } ?> <form action="login_check.php" method="post"> <div class="form-item"> <label for="ID"></label> <input type="text" name="id" required="required" placeholder="ID"> </div> <div class="form-item"> <label for="password"></label> <input type="password" name="password" required="required" placeholder="パスワード"> </div> <div class="button-panel"> <input type="submit" class="button" title="ログイン" value="ログイン"> </div> </form> </div> </html>2. login_check.php
入力したID、パスワードが事前に登録されているユーザのものなのかを認証します。
ログイン認証(login_check.php)<?php require_once("function.php"); session_start(); header("Content-type: text/html; charset=utf-8"); //パラメーター取得 $id = $_POST['id']; $password = $_POST['password']; //ログイン判定 //DB接続 try { /*dbname: test host: localhost (DBをローカル環境に置いている) username: root password: '' (未設定) */ $pdo = new PDO('mysql:dbname=test;host=localhost;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ] ); $stmt = $pdo->query('SELECT id, password, name FROM user'); $count = 0; while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $db_id = $row["id"]; $db_password = $row["password"]; $db_name = $row["name"]; $count = $count + 1; //ログイン失敗 if ($count == 0) { $_SESSION["error_status"] = 1; header("HTTP/1.1 301 Moved Permanently"); header("Location: login.php"); exit(); } // パスワードチェック // パスワードが一致したら「1」を、不一致なら「0」を返す if(password_verify($password, $db_password)) { $hantei = 1; } else { $hantei = 0; } if ($id == $db_id && $hantei == 1) { // ログイン成功 // セッションに格納することで、ログインユーザ情報をログイン先画面で使用することが可能 //セッション ID の振り直し session_regenerate_id(true); //セッションに ID を格納 $_SESSION['id'] = $id; //セッションに name を格納 $_SESSION['name'] = $db_name; //CSRF のトークン作成 $_SESSION["token"] = get_csrf_token(); //リダイレクト header("HTTP/1.1 301 Moved Permanently"); header("Location: 〇〇.php"); // ログイン先 exit(); } } } catch (PDOException $e) { exit('データベース接続失敗。'.$e->getMessage()); } //ログイン失敗 $_SESSION["error_status"] = 1; header("HTTP/1.1 301 Moved Permanently"); header("Location: login.php"); ?>3. function.php
function.php<?php //define("DNS","mysql://user01:0000@localhost/Test?charset=utf8"); define("SERVER", "localhost"); define("STRETCH_COUNT", 1000); /* * CSRF トークン作成 */ function get_csrf_token() { $TOKEN_LENGTH = 16; //16*2=32byte $bytes = openssl_random_pseudo_bytes($TOKEN_LENGTH); return bin2hex($bytes); } /* * パスワードをソルト+ストレッチング */ function strechedPassword($salt, $password){ $hash_pass = ""; for ($i = 0; $i < STRETCH_COUNT; $i++){ $hash_pass = hash("sha256", ($hash_pass . $salt . $password)); } return $hash_pass; } /* * ソルトを作成 */ function get_salt() { $TOKEN_LENGTH = 4;//4*2=8byte $bytes = openssl_random_pseudo_bytes($TOKEN_LENGTH); return bin2hex($bytes); } /* * URL の一時パスワードを作成 */ function get_url_password() { $TOKEN_LENGTH = 16;//16*2=32byte $bytes = openssl_random_pseudo_bytes($TOKEN_LENGTH); return hash("sha256", $bytes); } ?>環境
windows10 64bit
実行環境: xampp (mysql、php、apache等をまとめてインストールでき、インストールも簡単なので)おわりに
こういう系の記事は初めてなので、見よう見まねで書きました笑笑
抜けている箇所がありましたら、コメント欄で教えていただくとありがたいです。次回は、ログインアカウントの登録機能について記事を書きたいと思います。
- 投稿日:2020-08-07T00:04:06+09:00
連想配列のデフォルト値を省略すると実行速度は速くなる
気になったこと
以下のように引数の連想配列に不足しているキーを自動で補ってくれる関数がある。
f.phpfunction f($option = []) { $option += [ 'text1' => '1', 'text2' => '2', 'separator' => '/', ]; return implode($option['separator'], [$option['text1'], $option['text2']]); }関数の呼び出し時に(a)オプション配列にすべてのキーを指定する場合、(b)デフォルト値を省略した場合、どちらの実行速度が速いか?
時間計測スクリプト
benchmark.php$t1 = microtime(true); for($i=0;$i<500000;$i++) { f([ 'text1' => 'abc', 'text2' => 'def', 'separator' => '/', ]); } $t2 = microtime(true); $t3 = microtime(true); for($i=0;$i<500000;$i++) { f([ 'text1' => 'abc', 'text2' => 'def', ]); } $t4 = microtime(true); echo ($t2-$t1) . "\n"; echo ($t4-$t3) . "\n"; exit;結果
条件 実行時間avg (sec) (a)オプション配列にすべてのキーを指定する場合 0.12881302833557 (b)デフォルト値を省略した場合 0.12693285942078
- デフォルト値を省略したほうが実行速度は(若干)速い。
- フレームワーク(CakePHP, Laravelなど)の関数のように中身が複雑だと、さらに差が広がる。
- デフォルト値は省略したほうが実行速度は速いが、フレームワークのバージョンアップでデフォルト値が変わり、デグレが起きる可能性に留意する。