- 投稿日:2021-06-23T19:57:11+09:00
PHP Discord OAuth2認証
PHP Discord OAuth2認証 PHP Laravel環境でDiscord OAuth2認証を行ったので備忘録 環境 ・Larave 5.7 ・PHP 7.2.15 前提 ・Discord Developerで「OAUTH2_CLIENT_ID」「OAUTH2_CLIENT_SECRET」を取得 ・Discord DeveloperでCallback URLの設定 ログイン時 public function login(Request $request) { define('OAUTH2_CLIENT_ID', '<OAUTH2_CLIENT_ID>'); define('OAUTH2_CLIENT_SECRET', '<OAUTH2_CLIENT_SECRET>'); $authorizeURL = 'https://discord.com/api/oauth2/authorize'; $tokenURL = 'https://discord.com/api/oauth2/token'; $apiURLBase = 'https://discord.com/api/users/@me'; $params = array( 'client_id' => OAUTH2_CLIENT_ID, 'redirect_uri' => '<Callback URL>', 'response_type' => 'code', 'scope' => 'identify guilds' ); // Redirect the user to Discord's authorization page header('Location: https://discord.com/api/oauth2/authorize' . '?' . http_build_query($params)); die(); } Callbackで呼び出す関数 public function message_login_callback_discord(Request $request) { define('OAUTH2_CLIENT_ID', '<OAUTH2_CLIENT_ID>'); define('OAUTH2_CLIENT_SECRET', '<OAUTH2_CLIENT_SECRET>'); $authorizeURL = 'https://discord.com/api/oauth2/authorize'; $tokenURL = 'https://discord.com/api/oauth2/token'; $apiURLBase = 'https://discord.com/api/users/@me'; $apiURLGuild = 'https://discord.com/api/users/@me/guilds'; if($request->input('code')) { // 初回のCallback $token = $this->apiRequest($tokenURL, array( "grant_type" => "authorization_code", 'client_id' => OAUTH2_CLIENT_ID, 'client_secret' => OAUTH2_CLIENT_SECRET, 'redirect_uri' => '<Callback URL>', 'code' => $request->input('code') )); // sessionにaccess_tokenを格納 session(['access_token' => $token->access_token]); return redirect('/message_login_callback_discord'); } //上記処理後のCallback if(session('access_token')) { $user = $this->apiRequest($apiURLBase); $guilds = $this->apiRequest($apiURLGuild); return $user; } return redirect('/login'); } その他「API Request 関数」 public function apiRequest($url, $post=FALSE, $headers=array()) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); $response = curl_exec($ch); if($post) curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post)); $headers[] = 'Accept: application/json'; if(session('access_token')) $headers[] = 'Authorization: Bearer ' . session('access_token'); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $response = curl_exec($ch); return json_decode($response); } おまけ「特定のDiscordサーバーに参加しているか確認」 $user = $this->apiRequest($apiURLBase); $guilds = $this->apiRequest($apiURLGuild); $check_suiseicord_join = false; foreach ($guilds as $guild) { if ($guild->id=="<確認したいサーバー(guild)のID>") { $check_suiseicord_join = true; } }
- 投稿日:2021-06-23T18:50:52+09:00
【AIZU ONLINE JUDGE】Webエンジニアがアルゴリズムの問題を解いてみる
目次 自己紹介 AIZU ONLINE JUDGEとは やろうと思った経緯 問題例 所感 自己紹介 新卒3ヶ月目Webエンジニア(バックエンド) バックエンド未経験で自社開発系企業に入社 大学では情報工学を専攻.フロントエンドも少し触る 現在は社内の案件に触れて実務を行っている AIZU ONLINE JUDGEとは? AOJとよばれるもので、だれでもプログラミングの問題が解けるサイトです。 問題の内容はプログラミングコンテストのようなところで出される、お題に沿ってプログラムをつくって提出→採点をしてくれるサイトです。同じようなサイトに競技プログラミングのAtCoder( https://atcoder.jp/?lang=ja )なんてものもあったりします。 やろうと思った経緯 大学の専攻が情報工学で、よく課題に使われていたサイトだったのでそこで認知 ↓ web系の言語でも自分の実力を測りたい!もっと早く正確にコーディングできるようになりたい! ↓ せっかくなので取り組んだ手ごたえをアウトプット 問題例 今回は学生時代よく触れていた「アルゴリズムとデータ構造」という分野で一問取り組んでみたいと思います。 画像の通り分野ごとに何十問と問題があるのですが最初の「1_A:Insertion Sort」(挿入ソート)を解いていこうと思います。 アルゴリズムがあまりわからない方へ せっかく具体例を出して取り組むのでちょっとした解説を。 今回取り組む問題は"ソート"という配列等の並び替えをする手法の一つです。問題にもなっている挿入ソートの他にもマージソートやクイックソートなど、並び替えひとつとっても手法がいろいろありまして、計算速度やデータの形に合わせて使い分けます。 この挿入ソートは並べ替えの際に自然に思いつく手法で、配列の最初から順に大小を見ていって徐々にソートしていく手法です。 内容 問題はこのようになっていて実行時間の制限や使用メモリの制限もあります。 最初にアルゴリズムの概要、どのようなプログラムを作るのかが書いてあります。 続いて具体的な制約と入出力例があります。 問題説明で理解できなかった場合は具体例を眺めると意外と理解できたりします https://onlinejudge.u-aizu.ac.jp/courses/lesson/1/ALDS1/all/ALDS1_1_A 実際解いてみる プログラムはブラウザで直接かけるようになっていて、今回はPHPを使って書いていきます。 他にも多くの言語が対応しており、Web開発の言語はほぼ対応していました。 実際のコード↓ <?php $input = STDIN; //標準入力 $input_num = trim(fgets($input)); //1行目:要素数 $input_line = fgets($input); //2行目:入力値 $N = $input_num; $A = explode(" ",trim($input_line)); echo $input_line; //最初の一行 for($i = 1; $i < $N; ++$i){ $v = $A[$i]; $j = $i - 1; while($j >= 0 && $A[$j] > $v){ $A[$j+1] = $A[$j]; $j--; } $A[$j+1] = $v; //ループごとに進捗を出力 echo implode(" ",$A)."\n"; } コードを書き終え、”submit”ボタンを押すとこのようにチェックが入ります。問題なければACCEPTEDになり 問題があると指摘されます!今回は問題なく動作したので合格ということになりました。 アカウントを作ることでスコアが蓄積されるのでゲーム感覚で楽しめますよ! 所感 久しぶりにアルゴリズムの問題をといて思ったことは"よりシンプルに早く"という考え方を忘れてはいけないなと思いました。 webアプリケーションを開発している現場だとどうしてもフレームワークに頼っていて今回で言うソートのような裏側で何が動いているのかを知らずに使っている点も多くあると思います。ソートの解説で軽く触れたようにデータによっては相性が悪く相当計算に時間がかかってしまうアルゴリズムもあるので、”この裏では何が起こっているの?”という視点は少なからずエンジニアには必要なのかなと感じました。
- 投稿日:2021-06-23T16:04:57+09:00
phpフォームのページ遷移で$_SESSIONが消えないように気を付けたこと
はじめに php(今回フレームワークは使用していない)でフォームを作成したときに、CSRF対策(セキュリティ対策)でセッションを使用した。フォームは「ユーザ入力ページ」「入力内容確認ページ」「送信完了ページの3つ」。フォームの送信が入るたびにページ遷移が入るようにしたが、ページ遷移と同時に設定したセッションの値が切れてしまったので、対策したことを備忘録として残します。 作成したトークンと、そのトークンをセッション変数に代入してページ遷移後に同じ値であれば、ページを表示するというよくあるやり方。 結論 セッションに設定した変数は <?php の直後に書く。 対策したこと ディレクトリ構成 root/ ├index.php ├form-user-input.php ├form-confirm.php ├form-compleate.php └parts └header.php index.phpにそれぞれ入力、確認、送信完了ページをincludeして表示させるような構成です。 まず下記は、form-user-input.phpでフォームを入力してpost送信した時に、ページ遷移後、セッションが切れてしまったコードです。ヘッダーは共通のものを使用していたので、一番上に読み込んでいました。 index.php <?php include('./parts/header.php'); //共通のヘッダー読み込み session_start(); //セッションスタート $token = 'random_char'; //トークン生成、本番ではランダムな文字列を入れてます $_SESSION['token'] = $token; //$_SESSIONにトークンを代入 include('form-user-input.php'); //この下にフォームが送られた時の処理や、ファイル読み込みの処理を続けて書く form-user-input.phpからform-confirm.phpに遷移すると、$_SESSIONがNULLになっていました。原因がわからず悩んだ、、、ファイルを読み込む際には、session_start()やセッション変数を書いた後に書くとうまくいきました。 index.php <?php session_start(); //セッションスタート $token = 'random_char'; //トークン生成、本番ではランダムな文字列を入れてます $_SESSION['token'] = $csrf_token; //$_SESSIONにトークンを代入 include('./parts/header.php'); //共通のヘッダー読み込み include('form-user-input.php'); //この下にフォームが送られた時の処理や、ファイル読み込みの処理を続けて書く これでページ遷移後にも$_SESSIONの値はNULLではなく、生成したトークンと同じ値が入っていました。 おわりに 今回はページ遷移後にセッションが切れてしまう問題の解決方法を書きました。 セッションを用いたCSRF対策のコードの書き方を近いうちに記事にしようと思います。 また、本記事はあくまで僕が試してうまくいった一例なので、もっと他に効率いい方法があるかも、、 セッションについて詳しい方、ぜひコメント等で教えてくださいmm p.s フォーム作成はLaravel使うとセキュリティ対策の面でも多少書きやすくなるので、フレームワークおすすめです。
- 投稿日:2021-06-23T13:21:59+09:00
Laravel / Guzzle (HTTP Client) で画像ファイルを送信したい
Laravel + Guzzle を使用し、テキストや画像を混在させた状態のデータをHTTP Client経由で送りたいときの備忘録です。検証はしてませんがPDFやテキストファイルなど、その他のファイル形式でもいけるはずです。 APIでの画像アップロード(ファイルアップロード)のお供にどうぞ。 検証環境 Laravel 6.20.12 Guzzle 7.2 PHP 7.4 前提条件: base64 と multipart/form-data ファイルの送信にはよく base64 へのエンコードが用いられますが、Guzzleで画像やその他ファイルをエンコードせずに送るためには multipart/form-data という形式で送る必要があるようです。 base64 はざっくり言えば、64進数を意味しており、データを64種類の文字 (アルファベット[ a - z ], [ A - Z ], 数字[ 0 - 9 ]、一部の記号[ +, / ] )で表すエンコード方式です。 Base64 出典: フリー百科事典『ウィキペディア(Wikipedia)』 「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典 - Base64 base64ってなんぞ??理解のために実装してみた multipart/form-data は Content-Type(メディアの種別を表すもの)の一種で複合データ型を意味し、HTMLの送信フォームでファイルをアップロードしたいときなどによく見かけます。 下記のフォームでは enctype として multipart/form-data を設定しています。この設定がないと画像やPDFなど諸々ファイルアップロードができないので、設定を忘れてフォームが動かなかった、という経験のある方も多いのではないでしょうか。 <form action="/" method="post" enctype="multipart/form-data"> <input type="text" name="foo" value="foobar"> <input type="file" name="fooFile"> <button type="submit">送信</button> </form> HTML フォームにおける Content-Type - MDN - Mozilla 【HTTP】multipart/form-data の boundary って何ぞや? Multipart/form-dataの仕様メモ [フロントエンド] multipart/form-dataを理解してみよう SuikaWiki > Wiki > multipart/form-data (MIME) Guzzle公式ドキュメントより まずはコチラの公式ドキュメントを見てみます。英語だし端的にしか書かれていないので色々と不安になりますが、一度書いて動かしてしまえば大丈夫です。 Guzzle Docs: Request Options - multipart multipart のセクションで、以下のように書いてあります。 Sets the body of the request to a multipart/form-data form. (中略) The value of multipart is an array of associative arrays, each containing the following key value pairs: name: (string, required) the form field name contents: (StreamInterface/resource/string, required) The data to use in the form element. headers: (array) Optional associative array of custom headers to use with the form element. filename: (string) Optional string to send as the filename in the part. リクエストのボディを multipart/form-data 形式で設定します。 (中略) multipartの値は連想配列の配列であり、それぞれに次のキーと値のペアが含まれています。 name:(string, 必須)フォームのフィールド名 contents:(StreamInterface/resource/string, 必須)form要素で使用するデータ。 headers:(array)フォーム要素で使用するカスタムヘッダーのオプションの連想配列。 filename:(string)パーツのファイル名として送信するオプションの文字列。 オプションという表現も、慣れていないと「つまりどういうことだってばよ」という気持ちになったりしますが、元の英文の headers と filename に Optional と書いてあり、そういうときは大体、必須では無い、あってもなくても良い、必要に応じて入れる、という意味になります。 なので、今回の実装で必須なのは name と contents だということになります。 実装 Laravel 7 以上のバージョンでは、デフォルトでGuzzleのパッケージを含むそうで、使い方は以下のドキュメントに書かれています。 Laravel 7.x HTTPクライアント 今回はLaravel 6 以下での話になるので、Laravel 6 以下のバージョンは composer コマンドで guzzlehttp/guzzle パッケージのインストールを行います。 composer require guzzlehttp/guzzle 簡単に書くと、Guzzleを使ったリクエスト(画像ファイル送信)はこんな感じです。 一度Storageに保存した画像をGuzzleで読み込みさせるように実装しました。 use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Storage; use GuzzleHttp\Client; use GuzzleHttp\Psr7; public function postApi($data) { $method = 'POST'; $api_url = 'https://example.com/v1/upload'; $params = [ 'name' => 'token', 'contents' => Auth::user()->token ], [ 'name' => 'product_id', 'contents' => $data['product_id'] ], [ 'name' => 'image_file', 'contents' => Psr7\Utils::tryFopen('../' . Storage::url($data['file_path'])) ]; $client = new Client(); $response = $client->request($method, $api_url, ['multipart' => $params, 'http_errors' => false]); return $response; } headers/Content-Typeについて Guzzle のリクエストタイプには query や form_params を使用することの方が多いかもしれませんが、画像をそのままファイルとして送信したいときは multipart を使用します。 Stack Overflow など見ていると http における headers の内容を配列で別途指定している方もいるようですが、Guzzle ではリクエスト方式に multipart を選んだ時点で Content-Type が multipart/form-data になるため、あえて headers の Content-Type を手動で再設定する必要はありません。 認証などで headers をカスタマイズしなければいけないときのみ手動で設定してください。またその際は、Content-Type を誤った形式で上書きしないように注意してください。 パラメータについて name にはAPIのパラメータ名(フィールド名)、contents にはパラメータとして送りたい内容を入れます。 multipart のときは、ただのテキスト情報も、画像などのファイルも、一緒に送れます。 filename には任意でファイル名を入れられますが、なくても大丈夫です。 ファイルパスについて Guzzleが app ディレクトリと同じ並びの vendor ディレクトリにあるせいなのか、 Storage::url() だけだとパスエラーになったため、パスの先頭に ../ を付けています。(他にもっと良い書き方をご存知の方がいたら教えてくださいmm) その他のパラメータなど ほかに解説記事が豊富にあってここで説明するほどでもないかなと思った項目は飛ばしていますが、「この設定が知りたい」といったリクエストが万が一あれば(素人の解説でよければ)追記します。 という訳で今日はいったんここまで。 不備不足にお気付きの方はご指摘ください。 参考URL Guzzle Docs: Request Options - multipart POST - MDN - Mozilla PHP GuzzleHttp. How to make a post request with params? Multiple files uploaded via Guzzle multipart/form-data request are not recognized by Symfony Sample POST request with Guzzle 【PHP】GuzzleでPOSTリクエストができなかったお話し PHP Http ClientのGuzzleパッケージを使う 今時のPHP HTTPクライアントのGuzzleを使ってみた Guzzleを使って外部APIへPOST送信
- 投稿日:2021-06-23T13:08:53+09:00
Laravelスケジュールの実行ログを出力する
概要 Laravelでスケジュール機能を使って定期実行する際、実行ログを出力したいといったことがあったので調べたことをメモしておく。 ファイル構成 こんな感じでdockerのcron環境を整備する。 ※ ソースコード: Github-reflet/laravel5.6 ├ docker // ← docker関連のフォルダ │ └ cron │ ├ cron.root │ ├ Dockerfile // ← cron実行するコンテナ (PHP-CLI) │ └ php.ini │ ├ src // ← Laravel関連のフォルダ │ ├ app │ │ ├ Console │ │ │ ├ Commands │ │ │ │ ├ EmailSendCommand.php // ← メールをテスト送信するコマンド │ │ │ │ └ Schedule.php // ← schedule:runコマンド を上書きする │ │ │ └ Kernel.php // ← スケジュール設定 │ │ ... │ │ ├ resources │ │ └ views │ │ └ emails │ │ └ test.blade.php // ← テストメール文面 │ │ ... │ │ └ yarn.lock │ └ docker-compose.yml コマンド作成 テストメールを送信するプログラムを追加する。 $ docker-compose exec php php artisan make:mail TestEMail ./src/app/Mail/TestEMail.php protected $body; public function __construct(string $body = '') { $this->body = $body; } public function build(): TestEMail { return $this ->from('hoge@example.com') ->view('emails.test') ->with([ 'body' => $this->body ]); } 定期実行するテストメール送信コマンドを追加する。 $ docker-compose exec php php artisan make:command EmailSendCommand ./src/app/Console/Commands/EmailSendCommand.php <?php namespace App\Console\Commands; use Illuminate\Console\Command; use Illuminate\Support\Facades\Mail; use App\Mail\TestEMail; class EmailSendCommand extends Command { protected $signature = 'email:test'; ... public function handle() { $this->info(sprintf( '[%s] email:test command start.', Carbon::now()->format('Y-m-d H:i:s') )); $message = 'バッチ実行だよ。'; Mail::to('hoge@example.com') ->send(new TestEmail($message)); $this->info(sprintf( '[%s] email:test command complete.', Carbon::now()->format('Y-m-d H:i:s') )); } } スケジュール設定 Kernel.phpに設定を追加する。 ./src/app/Console/Kernel.php protected function schedule(Schedule $schedule) { // ↓ 1時間ごとに定期実行する $schedule ->command('email:test') // 実行するコマンド ->everyMinute() // 実行間隔: 毎分 ->environments(['local']) // ローカル環境でのみ実行してみる ; } logging設定 ログにscheduleチャンネルを新規に追加する。 ./src/config/logging.php 'channels' => [ ... // ↓ これの設定を追加する 'schedule' => [ 'driver' => 'daily', 'path' => storage_path('logs/schedule.log'), 'level' => 'info', 'permission' => 0666, 'days' => 7, ], ... ] schedule:runコマンド Laravelの $ php artisan schedule:run コマンドを上書きする。 ./src/app/Console/Commands/Schedule.php <?php namespace App\Console\Commands; use Illuminate\Console\Scheduling\ScheduleRunCommand; use Illuminate\Console\Scheduling\Event; use Illuminate\Support\Facades\Log; class Schedule extends ScheduleRunCommand { public function __construct(\Illuminate\Console\Scheduling\Schedule $schedule) { parent::__construct($schedule); } protected function runEvent($event) { // ↓ スケジュール実行ログを出力する Log::channel('schedule') ->info(sprintf('Running scheduled command: %s', $event->getSummaryForDisplay() )); // ↓ スケジュール実行結果ログを出力する $handlers = Log::channel('schedule')->getLogger()->getHandlers(); foreach ($handlers as $handler) { // dailyのログローテートしているので、そのハンドラーから情報取得する if ($handler instanceof \Monolog\Handler\RotatingFileHandler) { // ログのパスを取得する $path = $handler->getUrl(); $event->appendOutputTo($path); // 実行結果をログ出力する } } parent::runEvent($event); } } 動作確認 cronの実行状況を確認してみる。 $ docker-compose logs cron cron_1 | Running scheduled command: '/usr/local/bin/php' 'artisan' email:test >> '/var/www/www.example.com/storage/logs/schedule-2021-06-23.log' 2>&1 ログの出力状況を確認してみる。 ./src/storage/logs/schedule-2021-06-23.log [2021-06-23 11:22:01] local.INFO: Running scheduled command: '/usr/local/bin/php' 'artisan' email:test >> '/var/www/www.example.com/storage/logs/schedule-2021-06-23.log' 2>&1 [2021-06-23 11:22:02] email:test command start. [2021-06-23 11:22:02] email:test command complete. 問題ないようです。 参考サイト Laravel 5.6 タスクスケジュール Laravel 5.6 Artisanコンソール Laravel 5.6 メール 以上
- 投稿日:2021-06-23T12:46:22+09:00
「やればできる!」と言われたときだけ「ビビビビビタミン!MATCH」と返すmatch式
はじめに こんにちは。先日兄から自動化処理の仕事を受注しました、筆者です さて、今回は初のネタ記事です でも一応PHP8に触れているので興味持っていただければそれだけでうれしいです MATCHという飲み物を知っていますか? ↓これ 自分は高校生の頃に飲んでいた記憶です。 食堂(購買)横に設置している自販機に夏になる現れる人気(主観)な飲み物です! 私だけですかね、なんか青春って感じがします 最近はお笑い芸人のティモンディの2人がCMをやっていますね! match式を知っていますか? これ↓ PHP8で追加された、関数ではなく、式です! (コマンドで関数のリファレンス参照しようとして気づきました、、あ、関数じゃないわ、これ ) $ php -v PHP 8.0.3 (cli) (built: Mar 5 2021 07:54:13) ( NTS ) Copyright (c) The PHP Group Zend Engine v4.0.3, Copyright (c) Zend Technologies with Zend OPcache v8.0.3, Copyright (c), by Zend Technologies $ php --rf match Exception: Function match() does not exist ざっくり説明すると、厳密なswitch文みたいな感じです。 ただ、使い方によってはあいまい差を持たせることもできます。 match式のメリット ざっと思いつくメリットは以下でして、フォールスルーは場合によってはスマート(主観)に書けるケースもあると思っているので、すべてが悪だと私は思っていませんが、バグの温床にもなるので無いと助かります。 フォールスルーしない 厳密な比較 返り値がある式 本題 ただただ、match式を使いたいだけに、「やればできる!」と言われたときだけ「ビビビビビタミン!MATCH」と返すmatch式を作ります! <?php echo match ($argv[1]) { 'やればできる!' => "ビビビビビタミン!MATCH\n", 'なせばなる' => "vitamin\n", 'うてばひびく' => "ティモンディ\n", default => null }; 実行 $ php match.php "やればできる!" ビビビビビタミン!MATCH $ php match.php "なせばなる" vitamin おわりに やればできたよ! それでは!
- 投稿日:2021-06-23T10:55:47+09:00
laravel 任意のフィールドの値が指定したものと一致していない時に除外するバリデーションルールを記載する方法
目的 laravelで任意のフィールドの値が指定したものと一致していない時に当該フィールドのバリデートをスキップする方法をまとめる 方法 フィールドcheckの値に「true」という文字列以外が格納されたとき以外に、フィールドinput_strの値のバリデーションをスキップしたい時は下記のように記載する。 input_strの値のバリデーションルールは「必須」と「文字列であること」とする。 下記はリクエストファイルのrules()メソッドの内容のみ抜粋して記載する。 HogeRequest.php /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'check' => 'required', 'input_str' => 'exclude_unless:check,true|required|string', ]; } 任意のフィールドの値が指定したものと一致していない時のみ除外してほしいバリデーションルールの頭にexclude_unless:フィールド名,指定された値を記載する。 参考文献 https://readouble.com/laravel/6.x/ja/validation.html
- 投稿日:2021-06-23T03:01:31+09:00
ララベルで久々にプロジェクトをクローンしたのでつまづきポインの備忘録(forMac)
自分のプロジェクトをクローンしてデータベースのモックアップ(テスト環境)をローカルに作成しようと思ったのですが、前にプロジェクトをクローンした時と同じ轍を踏んでいる気がしたのでいい加減覚えようと戒めの意味も込めて備忘録を作成します。 1.プロジェクトのクローン これは簡単ですねサクッとローカルにクローンします。 simple_db_mockup % git clone https://github.com/mar-gitacount/simple.git うまくいったぽいです。 Cloning into 'simple'... remote: Enumerating objects: 554, done. remote: Counting objects: 100% (554/554), done. remote: Compressing objects: 100% (307/307), done. remote: Total 554 (delta 304), reused 463 (delta 213), pack-reused 0 Receiving objects: 100% (554/554), 1.06 MiB | 4.62 MiB/s, done. Resolving deltas: 100% (304/304), done. 中身もチェックします。プロジェクトはsimple_db_mockup/simple直下にあるので、 移動して中身をチェックします。 % cd simple % ls README.md dist server.php app docker-compose.yml server.txt artisan flowmemo.txt serverlog.txt bootstrap package-lock.json src composer.json package.json storage composer.lock phpunit.xml tests composer.phar public config resources webpack.mix.js database routes とりあえずは大丈夫そうです。 2.MAMPの設定 ローカ環境ではMAMPを利用しており、ここでは割愛しますが、htdocs直下にプロジェクトをクローンしています。 ちなみに自分の環境ですと以下になります。 /Applications/MAMP/htdocs/s_p/simple_db_mockup/simple MAMPは大変便利でバーチャルホストの設定をすることでプロジェクトのホスト名を任意で設定できます。MAMPではNginxとApacheが提供されていますが、Apacheを利用を前提に説明します。 2-1バーチャルホストにプロジェクトの追加 バーチャルホストにhtdocs以下のパスとホスト名を設定します。 vi /Applications/MAMP/conf/apache/extra/httpd-vhosts.conf パスが/Applications/MAMP/htdocs/s_p/simple_db_mockup/simple/public で、エイリアスをdbmockとします。 portはMAMPの初期設定の80ポートを利用します。 ~ <VirtualHost *:80> DocumentRoot "/Applications/MAMP/htdocs/s_p/simple_db_mockup/simple/public" ServerName dbmock </VirtualHost> ~ hostsファイルを書き換える。 ホストファイルを書き換えてIPアドレスとホスト名を紐づけます。これはMAMPではなくMac側の設定です。ファイルの場所はprivate/etc/hostsとなります。 他の方はわかりませんが、おそらくデフォルトでは管理者権限出ないとファイル編集ができないのではと思います。 sudo vi /private/etc/hosts 以下のようなファイルが表示されるかと思います。 ~ 127.0.0.1 localhost 127.0.0.1 s_p 127.0.0.1 js_study 127.0.0.1 neomedia 127.0.0.1 liqid_hp 127.0.0.1 dbmock ←追加 ~ 3..envファイルを書き換える。 とりあえずアクセスしてみます。 ホスト名をdbmockとしましたのでdbmockとアドレスバーに入力します。 しかし接続しても画面が真っ白になってしまい、表示されません インターネッツで調べるとどうやらcomoseをインストールしないといけないみたいです。 %composer install Laravel Framework 8.18.1 Usage: command [options] [arguments] Options: -h, --help Display help for the given command. When no command is given display help for the list command -q, --quiet Do not output any message -V, --version Display this application version --ansi Force ANSI output --no-ansi Disable ANSI output -n, --no-interaction Do not ask any interactive question --env[=ENV] The environment the command should run under -v|vv|vvv, --verbose Increase t ~ とりあえず正常に行きました。 今度こそはと思い、dbmockに移動しますが、500servererrorとなります。 先ほどとは内容が変わりました。 またもやインターネッツの旅に出かけます。 どうやら.envファイルが存在しないのが問題のようです。前も同じことしたような、、 その上でキーを作成しないといけないようです。 プロジェクトファイル内に.env.exampleファイルがありますのでこれをコピーして.env ファイルを作成します。 cp .env.example .env % php artisan key:generate Application key set successfully. でもう一回接続しますが今度はデータベース関係のエラーが吐かれています。 自分的にはこの辺は何度もやっていたので予測はしていました。 4.データベース設定 今まで利用していたデータベースとは違う場所で作成し、実験的に作業したかったので, 新しく作業したかったので作成します。 4-1データベース作成 DB名はdbmockとします。コマンドラインでやるのは面倒だったので、phpmyAdminを利用して作成します。 4-2.設定ファイルの修正 .envファイルとconfig/database.phpを修正します。 .envファイル(修正前) DDB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 DB_DATABASE=mysql DB_USERNAME=root DB_PASSWORD=root DB_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock .envファイル(修正後) DDB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 DB_DATABASE=dbmock ←編集 DB_USERNAME=root DB_PASSWORD=root DB_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock 修正後マイグレーションします。 % php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (51.64ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (50.18ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (53.27ms) Migrating: 2020_12_12_192229_create_votings_table Migrated: 2020_12_12_192229_create_votings_table (26.06ms) Migrating: 2020_12_23_191814_create_articles_table Migrated: 2020_12_23_191814_create_articles_table (62.01ms) Migrating: 2020_12_29_161423_remove_article_title_from_articles_table Migrated: 2020_12_29_161423_remove_article_title_from_articles_table (34.97ms) Migrating: 2021_01_20_140834_change_aritcle_articles Migrated: 2021_01_20_140834_change_aritcle_articles (61.61ms) Migrating: 2021_04_16_130836_add_column_to_user_prf_table Migrated: 2021_04_16_130836_add_column_to_user_prf_table (30.66ms) Migrating: 2021_06_02_094739_create_sessions_table Migrated: 2021_06_02_094739_create_sessions_table (102.78ms) Migrating: 2021_06_15_130739_add_column_to_gunle_table Migrated: 2021_06_15_130739_add_column_to_gunle_table (28.16ms) テーブルもできています!! dbmodkにアクセスしてみます。大丈夫そうです。 以上です。
- 投稿日:2021-06-23T00:23:14+09:00
【Php】フォームからDBへデータ保存(2)
初めに phpでフォームの入力内容をDBに保存する方法について学習した内容のoutput用記事です。 ※内容に間違いなどがある場合はご指摘をよろしくお願いします。 ※こちらの記事はあくまでも個人で学習した内容のoutputとしての記事になります。 前回の記事: https://qiita.com/redrabbit1104/items/a6e57aa1fd1771ef90ff https://qiita.com/redrabbit1104/items/a3eaf2bba51fac0b3c51 https://qiita.com/redrabbit1104/items/6e9c85c7503ed9043f38 https://qiita.com/redrabbit1104/items/c9b7bb7a89922521c003 https://qiita.com/redrabbit1104/items/3c83883ab811d7bd1337 phpファイルの読み込みとDB保存のための関数 入力フォームの完了画面のあたりに前回作成したデータベースへ保存処理が書かれているphpファイルを読み込みます。 <?php require 'insert.php'; ?> また、insert.phpファイルにはDBに入力フォームの内容を保存する関数を作成します。関数の引数にはフォームの入力値が入っている\$_POSTを渡します。 <?php require 'insert.php'; insertPost($_POST); ?> insertPost()関数の作成と$paramsのvalueを修正 insert.phpファイルのデータベースにデータを保存する処理が書かれている部分をinsertPost関数の処理部分として{}で囲みます。 // insert.php <?php const DB_HOST = 'mysql:dbname=udemy_php;host=localhost:8889;charset=utf8'; const DB_USER = 'php_user'; const DB_PASSWORD = 'wlfkfakfk12'; function insertPost($request){ //追記 try{ $pdo = new PDO(DB_HOST, DB_USER, DB_PASSWORD, [ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => false, ]); }catch(PDOException $e){ echo $e -> getMessage() . "\n"; exit(); } $params = [ 'id' => null, 'your_name' => '田中', 'email' => 'test@test.com', 'url' => 'http://www.abc.com', 'gender' => '1', 'age' => '2', 'contact' => 'いい', 'created_at' => null ]; $count = 0; $columns = ''; $values = ''; foreach(array_keys($params) as $key){ if($count++>0){ $columns .= ','; $values .= ','; } $columns .= $key; $values .= ':'.$key; } var_dump($columns); var_dump($values); $pdo->beginTransaction(); try { $sql = 'insert into contacts ('. $columns .')values('. $values .')'; var_dump($sql); //string(143) "insert into contacts (id,your_name,email,url,gender,age,contact,created_at)values(:id,:your_name,:email,:url,:gender,:age,:contact,:created_at)" $stmt = $pdo->prepare($sql); $stmt->execute($params); $pdo->commit(); $result = $stmt->fetchall(); var_dump($result); } catch (PDOException $e) { $pdo->rollback(); echo $e; } } ?> また、\$paramsのキーに対するvalueの値を修正します。 //中略 $params = [ 'id' => null, 'your_name' => $request['your_name'], //修正 'email' => $request['email'], 'url' => $request['url'], 'gender' => $request['gender'], 'age' => $request['age'], 'contact' => $request['contact'], 'created_at' => null ]; //中略 参考サイト