- 投稿日:2021-03-29T23:27:47+09:00
生PHPについて知識を資産化していく①
前提として
- 自分が勉強してきた知識を保存するために書いています。
- 本当に初歩的な事から書いていってます。
- 万が一間違っている可能性もございますので、その時はご指摘頂ければ嬉しいです。
- 常時更新していく予定です。
- アウトプット用ですので、MAMPを使用していきます。
ブラウザに「test」表示させる
index.php<?php echo 'test'; ?>現在のPHPのバージョンと設定一覧を表示させる
index.php<?php phpinfo(); ?>数字と文字
- 数字は半角で入力する
- 文字はシングル(またはダブル)クウォーテーションをつける
index.php<?php echo 123; → 数字 echo('こんにちは') //文字 echo("こんにちは") //文字 ?>変数
「$」マークで書き始める
index.php<?php $test = 123; //「123」という数字が$testに代入される $tsuishi = 'こんにちは'; //「こんにちは」という文字が$tsuishiに代入される echo $test; //ブラウザに 123 と表示される echo $tsuishi; //ブラウザに こんにちは と表示される ?>※同じ変数は後から書いたものが上書きされる
index.php<?php $test = 123; $test = 456; echo $test; //ブラウザに 456 と表示される ?>型を確認する
var_dump()
index.php<?php $test = 123; $tsuishi = 'こんにちは'; var_dump($tsuishi); //$tsuishiの型を確認する → string(15)と表示される var_dump($test); //$testの型を確認する → int(123)と表示される ?>定数
変わらない文字、数
同じ定数があっても上書きされない。index.php<?php const MAX = 10; const MAX = 20; echo MAX; //10と表示 ?>配列(1行バージョン)
※プログラミングは0から始まるので、
- 0の場合1
- 1の場合2
- 2の場合3
index.php<?php $array = [1, 2, 3]; echo $array[1]; //2と表示 //中身を全部みたい場合 echo '<pre>'; //改行して表示させる var_dump($array); echo '</pre>'; ↓ array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } ?>配列(2行3列バージョン)
index.php<?php $array_2 = [ ['赤', '青', '黄'], ['緑', '紫', '黒'] ]; echo '<pre>'; var_dump($array_2); echo '</pre>'; ↓ array(2) { //[0],[1]の2つの配列がある [0]=> array(3) { //[0]の中に配列が3つある [0]=> string(3) "赤" [1]=> string(3) "青" [2]=> string(3) "黄" } [1]=> array(3) { //[1]の中に配列が3つある [0]=> string(3) "緑" [1]=> string(3) "紫" [2]=> string(3) "黒" } } // 紫を出したい場合 echo $array_2[1][1]; //[1]の配列の中の[1]の紫を出力する ?>連想配列
配列と書き方が違う。
配列
数字(順番固定)と値がセット。
$array[1];連想配列
キーと値がセット。
キー => 値
key => value
$array["key"];index.php<?php $array_member = [ 'name' => '本田', 'height' => 170, 'hobby' => 'サッカー' ]; echo $array_member['hobby']; //サッカーと表示 echo '<pre>'; var_dump($array_member); echo '</pre>'; ↓ array(3) { ["name"]=> string(6) "本田" ["height"]=> int(170) ["hobby"]=> string(12) "サッカー" } ?>※(例)学校に例えて考えてみる
2組それぞれの生徒の特徴を記述するindex.php<?php $array_member_2 = [ '本田' => [ 'height' => 170, 'hobby' => 'サッカー' ], '香川' => [ 'height' => 165, 'hobby' => 'サッカー' ] ]; //香川の身長を出したい時 echo $array_member_2['香川']['height']; //165と表示 ?>各組それぞれの生徒の特徴を記述する
index.php<?php $array_member_3 = [ '2組' => [ '本田' => [ 'height' => 170, 'hobby' => 'サッカー' ], '香川' => [ 'height' => 165, 'hobby' => 'サッカー' ] ], '3組' => [ '長友' => [ 'height' => 160, 'hobby' => 'サッカー' ], '乾' => [ 'height' => 168, 'hobby' => 'サッカー' ] ] ]; // 3組の長友のheightを出したい時 echo $array_member_3['2組']['長友']['height']; //160と表示 ?>このように段階で分けていくことにより、
クラス・組・学年・学校・都道府県などで分けてデータを引っ張ってこれる演算子(計算や判定)抜粋
四則演算子 (+, -, *, /, %) 比較演算子 (>, >=, +=, ===, !==) 論理演算子 (and, &&, or, ||, xor) != nullや、!= emptyはよく使う == は使わない === を使う //== → 一致 //=== → 型も一致 ++ でインクリメント (1ずつ増える)index.php<?php $test_1 = 7; $test_2 = 3; $test_3 = $test_1 + $test_2; $test_4 = $test_1 - $test_2; $test_5 = $test_1 * $test_2; $test_6 = $test_1 / $test_2; $test_7 = $test_1 % $test_2; echo $test_3; //4と表示 echo $test_4; //4と表示 echo $test_5; //21と表示 echo $test_6; //2.33333333...と表示 echo $test_7; //1と表示 ?>条件分岐
プログラムが得意なもの
条件分岐(信号機など)と繰り返しif文
index.php<?php if (条件) { 条件が真なら実行 } //例 $height = 90; if ($height == 90) { echo '身長は' . $height . 'cmです'; //身長は90cmです と表示 } ?>else文
index.php<?php if (条件) { 条件が真なら実行 } else { 条件が偽なら実行 } //例 $height = 90; if ($height === 95) { echo '身長は' . $height . 'cmです'; } else { echo '身長は' . $height . 'cmではありません'; //身長は90cmではありません と表示 } ?>elseif文
index.php<?php if (条件) { 条件が真なら実行 } elseif{ 上記条件が偽で、こちらが真なら実行 } else { 条件が偽なら実行 } //例 $signal = 'yellow'; if ($signal === 'red') { echo '止まれ'; } elseif ($signal === 'yellow') { echo '一旦停止'; } else { echo '進め'; } //一旦停止 と表示 ?>if文の中にif文
index.php<?php //例 $speed = 80; if ($signal === 'blue') { if ($speed >= 80) { echo 'スピード違反'; //スピード違反 と表示 } } ?>〜ではない(!==)
!= → ~ではない
!== → 型が同じかどうかも含め~ではない//例
index.php<?php $height = 91; if ($height !== 90) { echo '身長は90cmではありません。'; //身長は90cmではありません。 と表示 } ?>データが入っているかどうか
※empty → データが空かどうか確認する
index.php<?php //例 $test = ''; if (empty($test)) { echo '変数は空です'; //変数は空です と表示 } //例 $test = '1'; //文字 if (!empty($test)) { echo '変数は空ではありません'; //変数は空ではありません と表示 } //例 $test = '1'; if (empty($test)) { echo '変数は空です'; } else { echo '変数は空ではありません'; //変数は空ではありません と表示 } ※elseを使うとスマートではないので、if (!empty($test))の書き方が良い。 ?>AND(&&) と OR(||)
index.php<?php //例 AND(&&) $signal_1 = 'red'; $signal_2 = 'blue'; if ($signal_1 === 'red' && $signal_2 === 'blue') { echo '赤と青です'; //赤と青です と表示 } //例 OR(||) $signal_1 = 'red'; $signal_2 = 'yellow'; if ($signal_1 === 'red' || $signal_2 === 'blue') { echo '赤です'; //赤です と表示 } ?>三項演算子
条件 ? 真 : 偽
index.php<?php //例 $math = 80; $comment = $math > 80 ? 'good' : 'not good'; echo $comment; //not good と表示 ?>foreach
//複数の値
foreach(複数形 as 単数形)index.php<?php //例 $members = [ 'name' => '本田', 'height' => 170, 'hobby' => 'サッカー' ]; //valueのみ表示 foreach($members as $member) { echo $member; //本田170サッカー と表示 } //keyとvalueそれぞれ表示 foreach($members as $key => $value) { echo $key . 'は' . $value . 'です'; } //nameは本田ですheightは170ですhobbyはサッカーです と表示 ?>多段階の配列を展開
index.php<?php //例 $members_2 = [ '本田' => [ 'height' => 170, 'hobby' => 'サッカー' ], '香川' => [ 'height' => 165, 'hobby' => 'サッカー' ], ]; foreach($members_2 as $member_1) { foreach($member_1 as $member => $value) { echo $member . 'は' . $value . 'です'; } } //heightは170ですhobbyはサッカーですheightは165ですhobbyはサッカーです と表示 ?>繰り返し文
for, while
for文()
繰り返す数が決まっている
//処理の例for($i = 0, $i < 10; $i++) { 処理 }index.php<?php //例 for($i = 0; $i < 10; $i++) { echo $i; } //0123456789 と表示 ?>index.php<?php //break //例(5になったら止める) for($i = 0; $i < 10; $i++) { if ($i === 5) { break; } echo $i; } //01234 と表示 ?>index.php<?php //continue //例(5をスキップする) for($i = 0; $i < 10; $i++) { if ($i === 5) { continue; } echo $i; } //01234 と表示 ?>while文
繰り返す数が決まっていない
index.php<?php $j = 0; while($j < 5) { echo $j; $j++; } //01234 と表示 ?>switch文
- if文の方が見やすいので基本はif文を使うようにする
- caseの後にbreakを入れないとそのまま処理が走ってしまうので入れるようにする
- どれにも当てはまらない場合は default となる
index.php<?php $data = 1; switch($data) { case 1: echo '1です'; break; case 2: echo '2です'; break; case 3: echo '3です'; break; default: echo '1〜3ではありません'; } //1です と表示 ?>関数
関数は2種類
- 組み込み関数 → 準備してある関数
- ユーザー定義関数 → 自由に作れる関数
組み込み関数に関してはこちら
関数リファレンスfunction test(引数) { //処理 return 戻り値; }index.php<?php //インプット引数 なし //アウトプット戻り値 なし function test() { echo 'テスト'; } test(); //テスト と表示 ?>index.php<?php //インプット引数 あり //アウトプット戻り値 なし function getComment($string) { echo $string; } getComment('コメント'); //コメント と表示 ?>index.php<?php //インプット引数 なし //アウトプット戻り値 あり function getNumberOfComment() { return 5; } var_dump(getNumberOfComment()); //int(5) と表示 $commentNumber = getNumberOfComment(); echo $commentNumber; //5 と表示 ?>index.php<?php //引数 2つ //アウトプット戻り値 あり function sumPrice($int1, $int2) { $int3 = $int1 + $int2; return $int3; } $total = sumPrice(3, 5); echo $total; //8 と表示 ?>文字列関数
index.php<?php //文字列の長さ $text = 'abc'; echo strlen($text); //3 と表示 ?>index.php<?php //日本語 SJIS, UTF-8 3〜6バイト(文字1文字で3バイト) → strlen $text = 'あいうえお'; echo strlen($text); //15 と表示 ?>index.php<?php //マルチバイト → 単純に日本語の文字数(mb_strlen) $text = 'あいうえお'; echo mb_strlen($text); //5 と表示 ?>index.php<?php //文字列の置換 → str_replace() $str = '文字列を置換します'; echo str_replace('置換', 'ちかん', $str); //文字列をちかんします と表示 ?>index.php<?php //指定文字列で分割 → explode() $str_2 = '指定文字列で、分割します'; echo '<pre>'; var_dump(explode('、', $str_2)); //「、」で分割する echo '</pre>'; ↓ array(2) { [0]=> string(18) "指定文字列で" [1]=> string(15) "分割します" } ?>index.php<?php //正規表現 → preg_match() //文字列かどうか, 数字かどうか, 郵便番号, メールアドレスかどうかを確認する preg_match('/文字列/, $str_3'); ?>詳しくはこちらをご確認ください
正規表現パターンに使用可能な修飾子
webdesign leavesindex.php<?php //指定文字列から文字列を取得する → substr() //文字化けしないように取得する → mb_substr() echo substr('abcde', 1); //bcde と表示 echo mb_substr('あいう', 2); //う と表示 ?>配列の関数(array)
index.php<?php //配列に配列を追加する(array_push) $array = ['りんご', 'みかん']; array_push($array, 'ぶどう', 'なし'); echo '<pre>'; var_dump($array); echo '</pre>'; ↓ array(4) { [0]=> string(9) "りんご" [1]=> string(9) "みかん" [2]=> string(9) "ぶどう" [3]=> string(6) "なし" } //ぶどうとなしが追加される ?>PHPの配列関数に関してはこちら
図解でわかるPHP配列関数一覧関数名について
キャメルケースかスネークケースで関数名をつける。 //キャメルケース checkPostalCode() → 最初の英単語の頭文字は小文字、次の英単語から頭文字は大文字で書く //スネークケース check_postal_code() → 英単語ごとにアンダーバーで区切る ※基本的にはスッキリしているキャメルケースで書く!!組み込み関数
<?php //郵便番号のチェック $postalCode = '123-4567'; function checkPostalCode($str) { // ハイフンをなくすために空にする $replaced = str_replace('-', '', $str); // ハイフンがなくなった後の文字の数を取る $length = strlen($replaced); var_dump($length); //int(7) と表示 if ($length === 7) { return true; } return false; } var_dump(checkPostalCode($postalCode)); // bool(true) と表示 ?>変数のスコープ
有効範囲
例:
+ グローバル = 全世界
+ ローカル = それぞれindex.php<?php $globalValiable = 'グローバル変数です'; function checkScope() { $localValiable = 'ローカル変数です'; echo $localValiable; (関数の中で$globalValiableを指定しても使えない) //どうしても関数の外にあるグローバル変数を使う場合は、globalと前につける global $globalValiable; echo $globalValiable; //グローバル変数です と表示 } echo $globalValiable; //グローバル変数です と表示 echo $localValiable; //表示されない checkScope(); //ローカル変数です と表示 ?>※globalというキーワードを使うことはほとんどない
グローバル変数を使いたい場合は、インプットを引数に書くようにするとスマートに書ける
index.php<?php $globalValiable = 'グローバル変数です'; function checkScope($str) { $localValiable = 'ローカル変数です'; echo $str; } echo $globalValiable; echo $localValiable; checkScope($globalValiable); //グローバル変数です と表示 ?>ファイルの読み込み
require(); require_once(); //エラー include(); include_once(); //警告commom.php<?php $commonVariable = "共通の変数です"; function commonTest() { echo '外部ファイルの関数です'; } ?><?php require 'common.php'; echo $commonVariable; //共通の変数です と表示 commonTest(); //外部ファイルの関数です と表示 ?>マジック定数
- __DIR__ ← 現在のファイルの絶対パスを表示する
- __FILE__ ←現在のファイルを表す
index.php<?php __DIR__; //例:/Applications/MAMP/htdocs/***/ の中にファイルがあると判断できる __FILE__; //例:/Applications/MAMP/htdocs/***/index.php のファイルだと判断できる ?>次回へつづく(執筆中)
- 投稿日:2021-03-29T20:14:09+09:00
【PHP】確認画面で画像を保存せず表示
はじめに
ユーザー新規登録で確認画面に遷移した際に、
戻るボタン
を押すと画像だけが残ってしまう問題が発生しました。
こちらは確認画面で画像をフォルダに保存していたため、起きてしまいました。
そこで確認画面で画像を一時的にセッションに保存し、登録確定された場合のみ保存される上記動作画面のような処理を実装します。実装方法
画像情報をセッションに保存
user_add.php<h2>新規登録</h2> <form method="post" action="user_add_check.php" enctype="multipart/form-data"> <input type="text" name="name" class="user_name_input" placeholder="ユーザー名"> <input type="password" name="pass" class="user_pass_input" placeholder="パスワード"> <input type="file" name="image" id="my_image" accept="image/*" multiple> <input class="btn btn-outline-info" type="button" onclick="history.back()" value="戻る"> <input class="btn btn-outline-dark" type="submit" value="登録"> </form>今回は、このような登録画面で実装します。
user_add_check.php<?php session_start(); $_SESSION['image']['data'] = file_get_contents($_FILES['image']['tmp_name']); $_SESSION['image']['type'] = exif_imagetype($_FILES['image']['tmp_name']); ?> <img src="image.php"> <form method="post" action="user_add_done.php"> <input type="hidden" name="name" value="<?= $user_name ?>"> <input type="hidden" name="pass" value="<?= $user_pass ?>"> <input type="hidden" name="image_name" value="<?= $user_image['name'] ?>"> <input type="button" class="btn btn-outline-info modal_close" onclick="history.back()" value="戻る"> <input type="submit" class="btn btn-outline-dark" value="OK"> </form>確認画面で画像を出力するため、一時的にセッションへ保存して表示します。
<?php session_start(); $_SESSION['image']['data'] = file_get_contents($_FILES['image']['tmp_name']); $_SESSION['image']['tmp'] = exif_imagetype($_FILES['image']['tmp_name']); ?> <img src="image.php">確認画面での表示のため
$_SESSION['image']['data']
に画像データを渡します。
$_SESSION['image']['tmp']
は後ほど、確認画面で画像を表示の際にmimeタイプ取得のため用います。セッションに保存した情報を元に、画像を表示するファイルを作成します。
image.php<?php session_start(); switch ($_SESSION['image']['type']) { case IMAGETYPE_JPEG: header('content-type: image/jpeg'); break; case IMAGETYPE_PNG: header('content-type: image/png'); break; case IMAGETYPE_GIF: header('content-type: image/gif'); break; } echo $_SESSION['image']['data']; ?>セッションから画像情報を出力します。
これで画像を確認画面で表示することができ、
戻るボタン
を押しても保存される問題はございません。user_add_done.php$user_image = $_POST['image_name']; file_put_contents('./image/'.$user_image,$_SESSION['image']);最後に登録を確定した場合に、画像を指定のファイルに保存するようにします。
参考URL
- 投稿日:2021-03-29T17:34:50+09:00
Laravelのルーティングの状態をファイルで確認してみる
環境
バージョン等 Laravel 6.20.19 MacOS 10.15.7 Catarina ①普通にルーティングを確認してみる
Laravelの通常のWebページとしてのルーティングは
Routes/web.php
にいろいろと記載していますが、
prefixとかgroupeなどを記述していると、実際にどうなっているか分からなくなったりしますよね。これを確認する時に使うのが、
ターミナル$ php artisan route:list
というコマンド。
なのですが、これがずら〜と表示されてちょっと分かりづらい。
Before
②見やすくルーティングを表示する
ということでこれをファイルとして保存して見やすく表示してみましょう。
route:list
コマンドに> .route
を追加すればOKです。ターミナル$ php artisan route:list > .routeこれでプロジェクトの直下に.routeというファイルが保存されました。
After
中身を表示するとこんな感じで見やすく表示されます。
作成してしまえばその後はコマンドを実行せずに確認できるのも嬉しいですね。
なおルーティングにもし変更があれば、再度同じコマンドを実行すれば上書きされます。また作成した.routeファイルは、チームで共有する必要はないと思いますので.gitignoreに追加すると良いです。
.gitignore.route #追記
以上、小技ではありますが最後までお読みいただきありがとうございます〜!
- 投稿日:2021-03-29T17:34:50+09:00
【Laravel】ルーティングの状態をファイルで見やすく確認する小技
環境
バージョン等 Laravel 6.20.19 MacOS 10.15.7 Catarina ①普通にルーティングを確認してみる
Laravelの通常のWebページとしてのルーティングは
Routes/web.php
にいろいろと記載していますが、
prefixとかgroupeなどを記述していると、実際にどうなっているか分からなくなったりしますよね。これを確認する時に使うのが、
ターミナル$ php artisan route:list
というコマンド。
なのですが、これがずら〜と表示されてちょっと分かりづらい。
Before
②見やすくルーティングを表示する
ということでこれをファイルとして保存して見やすく表示してみましょう。
route:list
コマンドに> .route
を追加すればOKです。ターミナル$ php artisan route:list > .routeこれでプロジェクトの直下に.routeというファイルが保存されました。
After
中身を表示するとこんな感じで見やすく表示されます。
作成してしまえばその後はコマンドを実行せずに確認できるのも嬉しいですね。
なおルーティングにもし変更があれば、再度同じコマンドを実行すれば上書きされます。また作成した.routeファイルは、チームで共有する必要はないと思いますので.gitignoreに追加すると良いです。
.gitignore.route #追記
最後までお読みいただきありがとうございます〜!
- 投稿日:2021-03-29T17:30:41+09:00
PHP if文とcount()の混合条件の作成
はじめに
今回は基本的な投稿システムが完了したので少しこだわりを入れて備忘録として投稿いたします。これを利用してlaravelに組み込みます。
今回の条件
- 1件以上のコメントがある場合は現在〇〇件コメント数という風に表示
- コメントが0件の場合は現在〇〇件コメント数を非表示にする
これらの条件を使用するにはif文とcount()の両方を使用することになります。以下にサンプルを掲載します。
if文の構文
if.php$value1 = 200; $value2 = 100; if ($value1 > $value2){ echo 'value1はvalue2より大きい'; $value2 = $value1; }countの構文
count.php$collection = collect(['鈴木', '佐藤', '田中']); echo $collection->count(); // 3上記の条件を纏めたコード
sample.php@if ($post->comments->count() >= 1) <p> <span class="badge badge-primary"> 現在のコメント数 {{ $post->comments->count() }}件 </span> </p> @endif解説
今回は各々が投稿した投稿内容の詳細にコメントを掲載するようになっています。
$postは投稿の変数になります。comments
はPost.modelのcommentsを表しています。
>= 1
で1件以上のコメントがある場合は不等号を使用して表示させる様にしています。Post.phppublic function comments() { // 投稿は複数のコメントを持つ return $this->hasMany('App\Models\Comment'); }
- 投稿日:2021-03-29T17:01:51+09:00
switch文撲滅委員会はこの度match式推奨委員会になりました
はじめに
昔こんな記事を書いたことがあります。
switch文撲滅委員会
switch文には罠が多く極力使わない方が良い、ということを言いたくて書いたものです。
この度PHP8からmatch式
が使えるようになりました。
switch文を撲滅してmatch式を推奨していきたいのでこの記事を書きました。match式とは
公式ドキュメント
switch文と比較すると以下の違い(利点)があります。
switch match 比較 曖昧 厳密 break 必要 不要 例外値 スルー 例外 サンプルコード
比較
前回の記事で出したサンプルコード
sample1_switch.php<?php $sample = 1; switch($sample){ case true: echo 'これはtrueです'."\n"; break; case 1: echo 'これは数値の1です'."\n"; break; default: echo 'これは1ではありません'."\n"; break; } // 結果 // これはtrueですこのコードでは曖昧比較であるためcase trueの部分に入ってしまうのが課題でした。
ではmatch式で書くとどうなるでしょうか?sample1_match.php<?php $sample = 1; $return_value = match ($sample) { true => 'これはtrueです', 1 => 'これは数値の1です', default => 'これは1ではありません', }; var_dump($return_value); // 結果 // string(25) "これは数値の1です"自動で厳密比較をしてくれました。
フォールスルー
sample2_switch.php<?php $sample = 'a'; switch($sample){ case 'a': echo 'これはaです'."\n"; case 'b': echo 'これはbです'."\n"; case 'c': echo 'これはcです'."\n"; default: echo 'これはa,b,cではありません'."\n"; } // 結果 // これはaです // これはbです // これはcです // これはa,b,cではありませんこのコードではbreakを書いていないため全ての分岐が評価されるのが課題でした。
ではmatch式で書くとどうなるでしょうか?sample2_match.php<?php $sample = 'a'; $return_value = match ($sample) { 'a' => 'これはaです', 'b' => 'これはbです', 'c' => 'これはcです', default => 'これはa,b,cではありません', }; var_dump($return_value); // 結果 // string(16) "これはaです"breakなしでもフォールスルーされませんでした。
例外値
sample3_switch.php<?php $sample = 'd'; switch($sample){ case 'a': echo 'これはaです'."\n"; case 'b': echo 'これはbです'."\n"; case 'c': echo 'これはcです'."\n"; } // 結果なしdefaultが未定義でどのケースにも合致しない場合、switch文ではスルーされていました(何も起きない)
ではmatch式で書くとどうなるでしょうか?sample3_match.php<?php $sample = 'd'; $return_value = match ($sample) { 'a' => 'これはaです', 'b' => 'これはbです', 'c' => 'これはcです', }; var_dump($return_value); // 結果 // PHP Fatal error: Uncaught UnhandledMatchError: Unhandled match value of type string in sample3_match.php:3 // Stack trace: // #0 {main} // thrown in sample3_match.php on line 3どのケースにも合致しないとUnhandledMatchErrorの例外が投げられます。
これで意図しないケースにすぐに気付くことができ、より堅牢にコードを作成することが可能となりました。まとめ
match式は今までのswitch文の罠だった部分をキレイに回避できるため、大変おすすめです。
PHP8を使っている方は是非match式で書いていきましょう!付録
ローカルの環境をphp8にしたくなかったので挙動を試せるdocker環境を用意しました。
ご自由にお使いください。
https://github.com/yamamoto-hiroya/php8_practice
- 投稿日:2021-03-29T16:41:56+09:00
PHP5上級試験/準上級試験の上級合格に挑戦(14) プログラミングPHP第3版 3章〜4章
はじめに
- 3章関数に関しては、黒本に書いていないことはほぼないため省略し、4章文字列から
- 4章文字列だと、しれっと黒本に書いてない関数が出てきたりする
- 後半だと正規表現が出てくる。黒本だと18章。正規表現は結構使うので頑張って身につけていきたいところ。
4.1 文字列定数のクォート処理
バックスラッシュでエスケープされる対象が未知のものであった場合それはそのまま表示される
index.php$str = "これは\c なんだ?"; echo $str;出力
これは\c なんだ?4.2.3 printf
- 各書式に合わせて文字列を出力可能(小数第1位切り捨てとか、日付とか、0付き数値とか、16進数形式など)
- 出力せず、変数に文字列として返したい場合はsprintf()を使う。
index.php$date = new DateTIme('now'); $year = $date->format('Y'); $month = $date->format('m'); $day = $date->format('d'); $str = sprintf('%4d/%02d/%02d',$year,$month,$day); echo '今日は' . $str . 'です';出力
今日は2021/03/25です※もっと効率的なやり方あると思うけど今回はこれで
4.2.4 print_r()とvar_dump()
- GLOBALSのような再帰構造をもつものだと、print_fは無限ループになってしまうが、var_dump()は3回までやってそのあとは出力切り捨てになる
4.3 個別文字へのアクセス
strlen()を使い、Helloという文字を1文字ずつ出力させよ。
index.php$string = 'Hello'; for ($i=0; $i < strlen($string); $i++){ printf("%d番目の文字は %s です<br>",$i,$string{$i}); printf("%d番目の文字は %s です<br>",$i,$string[$i]); }※これは私も驚いたが、変数{}や変数[]で、中にある数値(0から)でそれぞれの順番にある文字が抜き出せる仕様らしい
4.4.1 空白の除去
空白の除去といえばtrim系関数だが、これにはオプションがつけられる。
デフォルトだと前後の空白しか削除しないが、オプションを指定するといろいろなものが削除可能。index.php$record = " Fred\tFlintstone\t35\tWilma\t \n"; $record = trim($record, " \r\n\0\x0B"); echo $record;出力
Fred Flintstone 35 Wilma
// Fred\tFlintstone\t35\tWilma にならない?? というより\tが除去されてる感じ
なぜ??4.6.2 類似性
2つの文字列が似ているかを調べる関数がphpには存在する
そこで問題(1) ある単語が英語でどう発音されるかに基づいた値を出力する関数は?
(2) (1)のうち性能がよいのは?
(3) 2つの文字列に共通している文字の数を返す関数は?
(4) 2つの文字列を一致させるために何文字を修正すればいいかを計算することで類似性を出力する関数は?
解答
(1) soudex(),metaphone() サウンデックス・メタフォン
(2) metaphone()
(3) similar_text()
(4) levenshtein() リーベンシュタイン以下、例
なお、soundexとmetaphoneだが、同時に使うことはできず、比較する時は必ずどちらかに統一しなければならない。index.php//(1) $name1 = "Fred"; $name2 = "Phred"; if (soundex($name1) == soundex($name2)){ print "soundex: {$name1}と{$name2}は似てる!<br>"; } else { print "soundex: {$name1}と{$name2}は似てないな<br>"; } if (metaphone($name1) == metaphone($name2)){ print "metaphone: {$name1}と{$name2}は似てる!<br>"; } else { print "metaphone: {$name1}と{$name2}は似てないな<br>"; }index.php//(2) $name1 = "Rasmus Lerdorf"; $name2 = "Razmus Lehrdorf"; $common = similar_text($name1,$name2,$percent); printf("2つの文字列は %d 文字(%.2f%%)一致",$common,$percent);index.php//(3) echo $similarity = levenshtein('cat','cot');4.7.3.3 sscanf
printfはフォーマットに沿って表示させるが、こっちは、フォーマットに沿って文字列を分解し、配列に入れるという動作を行う。
たとえば、次の例は日時を分解した例。index.php$x = '12/31 23:59'; $y = sscanf($x,'%d/%d %d:%d'); echo $y[0], '月'; echo $y[1], '日'; echo $y[2], '時'; echo $y[3], '分';出力
12月31日23時59分下はオプションの変数をつけた場合、返り値は要素(フィールド)数になる。
index.php$x = '12/31 23:59'; $y = sscanf($x,'%d/%d %d:%d',$month,$day,$hour,$minute); echo "{$y} 個のフィールドに分解しました<br>"; echo "{$month}月{$day}日{$hour}時{$minute}分";4.7.4.3 マスクを使う関数
黒本にも載ってる内容だが、strcspn()はNULバイトやタブ、キャリッジ・リターンが含まれてないかを調べるのに有効。
index.phpecho strcspn($str,"\n\t\0") != strlen($str);というように使う。
NULバイトやタブ・キャリッジ・リターンが含まれていないかを調べることができる。4.8 正規表現
正規表現=検索のパターンを表す文字列。このパターンを別の文字列と比較し、その文字列がそのパターンにマッチするか調べる関数がある
使用方法としては大まかに「マッチした情報のゲット」、「マッチした内容を別の文字列への置換」、「マッチした文字列をいくつかに分離する」という3つがある。
PHPで使用可能な正規表現
(1) PHPで使用可能な正規表現を3つ
(2) (1)はそれぞれどのような拡張モジュールを持っているか?
(3) (1)の各正規表現の構文モードは?
(4) 日本語を含むマッチングのために必要なものは?
解答
(1) mbstringの正規表現、Perl互換の正規表現,POSIXの正規表現
※なおPOSIXはPHP5.3.0で非推奨
(2) mbstring拡張モジュール、PCRE(ペクル)拡張モジュール、regex(リジェックス)拡張モジュール
※mb_で始まる関数、preg_で始まる関数、ereg_で始まる関数である。
※PCREはC言語のライブラリ。
(3) Ruby,Perl,regex
※デフォルトだが、切り替えも可能
(4) mbstring以外だと、文字エンコーディングにUTF−8,オプション「U」を指定する正規表現の基本
- NULLバイトが文字列の終わりの記号と判断され、認識されない時がある
→バイナリセーフな関数で対応可能- 文字クラス[]/選択肢|/繰り返し/アンカー/デリミタ/量指定子は基本。
参考:サルでもわかる正規表現
ありがとうございます!- 言明(assertion)とは、パターンの前後に対する条件付け。アンカー、言明サブパターン(4.8.15参照)がある。
(1) 量指定子で{n,m} {n} {n,} の意味は?
(2) アンカーで、[[:<:]] [[:>:]] \b \B \A \z \Z の意味は?
(3) 文字クラスで\s \S \w \W \d \D の意味は?
解答
(1) 最低n回、m回以下 最低n回、n回以上
(2) 単語先頭/単語末尾/ワードの境界/ワードの境界以外/文字列の先頭/文字列の末尾(改行の直前)/文字列の末尾
(3) 空白文字/空白文字以外/ワード文字/ワード文字以外/数字/数字以外文字クラス
(1) 照合順序とは?
(2) 等価クラスとは?解答
(1) ロケール、つまり地域によっては特定の文字列を1つの文字として扱うことがあるが、これを照合順序という。
たとえば、スペイン語でchは1つの文字として扱われている。 a,b,ch,c、d・・というように。
とすると、文字クラスで[st[.ch.]]は、sかtかchのどれかにマッチするということになる。(2) 等価クラスは、たとえば[a/a(ウムラウトつき)/a(アクセントつき)]の場合、並び順はaという区切りで行われる。
4.8.11 キャプチャしないグループ
(?:サブパターンさせる言葉)とする
下記はキャプチャさせるグループとキャプチャさせないグループの比較index.phpecho preg_match("/(ello)(.*)/","jello biafra",$match1); print "<br>"; echo $match1[1]; print "<br>"; echo preg_match("/(?:ello)(.*)/","jello biafra",$match2); print "<br>"; echo $match2[1];出力
1
ello
1
biafra理由として、まず1つ目でマッチするのは
ello biafra,ello,biafraの3つ。
2つめは
ello biafra,biafraの2つだけ。
理由はおそらく、1つ目の場合、(ello)がキャプチャされ、2つ目で出力されたからだろう。
2つ目の場合はelloがキャプチャされなかったので出力されず、biafraだけ出力されたってことか。4.8.12 後方参照
パターンの中で取得したテキストを同じパターンで後の方にないか探すことができる
index.phpecho preg_match("/([[:alpha:]]+)\s+\\1/","Paris in the the spring",$m); print "<br>"; echo $m[1];出力
theまず最初と最後の/はデリミタ。
( )で囲まれたサブパターンは、アルファベットが1文字以上というのを表している。
そしてその後の\sはスペース。が+で一文字以上続くことを表す。
\1はバックスラッシュはエスケープで、後方参照、つまりさっきマッチした同じ文字が出てくることを表している。
これを全部確認すると、
たとえば、Paris Parisとか in in、the the とかspring springだったら、マッチするということ。
で、ここではthe theがマッチする。
ここで、echoは、$m[1]、つまりサブパターンなので、theが出力される。ここは理解するのに悪戦苦闘した(こちら参照)
助けていただいた@blue32aさん、ありがとうございました。
要するにパターンがコピーされるというわけではないというのを勘違いしてたということ。
つまり、/([[:alpha:]]+)\s+\1/ は、/([[:alpha:]]+)\s+([[:alpha:]]+)/じゃないの? という。
そうではなくて、()内で取得できたワードがそのまま\1にくる。それがパターンになる。4.8.13 後置オプション
閉じデリミタの後に置ける1文字のオプション。これによって挙動が変わる。
index.phpecho preg_match("/cat/i", "Stop,Catherine!");出力
1
Catherineの「Cat」は大文字であるが、大文字小文字を区別しなくなったのでcatとマッチする。以下はどんな挙動を起こすものか?
(1) i
(2) s
(3) x
(4) m
(5) e
(6) U
(7) u
(8) X
(9) A
(10) D
(11) S
解答
(1) 大文字小文字を区別しない
(2) ピリオドを改行を含めた任意の文字にマッチさせるようにする。
通常は改行は含めない。
(3) パターンから空白・コメントを除去する
(4) キャレットは各改行直後の文字、ドルは各改行直前の文字にマッチさせる。(いわゆる複数行モードのこと)
(5) 置き換え文字にPHPを指定すると、そのコードをevalした結果で文字列を置き換える。
(6) 貪欲さの指定を反転する。*や+の挙動を貪欲さを最低にする
(7) パターンをUTF-8として扱う
(8) バックスラッシュ直後に特殊文字でないものが続くとエラーを起こすようにする。
(9) パターンの最初の文字が^であるように文字列の先頭にアンカーを設定する
(10) $を行末のみマッチさせるようにする
(11) パターンを解析をじっくりして、次からの処理を早くする。なお、オプションは複数設定できる。たとえば/imのように。
4.8.14 インラインオプション
上記以外にもパターンの一部にだけ適用するオプションを設定できる。
フォーマットは(?(フラグ名):(パターン))
使えるのはi,m,s,U,x,Xindex.phpecho preg_match('/I like (?i:PHP)/','I like pHp');もちろん1を返す。
逆に上記でオプションの前にハイフン(-)をつけることでその設定を無効にできる。index.phpecho preg_match('/(?-i:I like) PHP/i','I like pHp');もちろん1を返す。
index.phpecho preg_match('/I like (?i)PHP/',I like pHp'); echo preg_match('/I (like (?i)PHP)/',I like pHp a lot',$match); echo $match[1];※ただし、このインラインオプションが設定された部分(カッコ内)はキャプチャの対象にならず、キャプチャしたい場合は、別に()を用意する必要がある。
4.8.15 先読みと戻り読み
もし次(前)の文字が○○だったら、これをマッチさせるという機能。
文字列を分解するときに便利
これらはどういう意味か?
- パターンX(?=パターンY)
- パターンX(?!パターンY)
- (?<=パターンY)パターンX
- (?<!パターンY)パターンX
解答
+ Xの直後でYがマッチする
+ Xの直後でYがマッチしない
+ Xの直前でYがマッチする
+ Xの直前でYがマッチしない下記は例
index.php$message = preg_split('/(?=^From )/m' $mailbox);メールボックスでメール内容を取得する方法。
Fromで始まるところが分離できる点なのでこれが効果的。4.8.16 無視
繰り返しを含む式を更に繰り返さないようにする
FMTは(?>subpattern)。
つまりマッチした部分を更に繰り返さないようになる。
ただ、出力方法は以前と変わらず、時間が短くなるだけ。
下記は前者が無視を使わないパターンの例、後者が無視を使ったパターンの例
どちらも出力はN。
時間が短くなるかどうかはこのサンプルでは測れず。sample.php$p = '/(a+|b+)*\.+$/'; //$p = '/(?>a+|b+)*\.+$/'; $s = 'ababbbababababababababababababa..!' if (preg_match($p,$s)){ echo 'Y'; }sample2.php/*$p = '/(a+|b+)*\.+$/';*/ $p = '/(?>a+|b+)*\.+$/'; $s = 'ababbbababababababababababababa..!'; if (preg_match($p,$s)){ echo 'Y'; } else { echo 'N'; }
4.8.17 条件式
正規表現で使用可能なif文のようなもの。
(?(条件)yespattern)
(?(条件)yespattern/Nopattern)
※なお、条件として使用可能なのは後方参照(1~99まで)・先読み・戻り読みのみ。index.php(?(?=[^a-z]*[a-z]) \d{2}-[a-z]{3}-\d{2} | \d{2}-\d{2}-\d{2} )上記はYes/Noを使ったもの。
最初ので、数値が続いた後にそれがあってもなくてもよく、その後にアルファベットが続くものにマッチするか調べる。
つまり、数値のあとに少なくとも英字が1文字以上あるかを調べる。
この場合、-があるかどうかは条件式の時点では問題ではない、ということ。マッチしたら、次にYesの場合はdd(数字)-aaa(アルファベット)-dd(数字)にマッチするか調べる
Noの場合はdd(数字)-dd(数字)-dd(数字)にマッチするか調べる。この場合、dd(数字)-aaa(アルファベット)-dd(数字)か、dd(数字)-dd(数字)-dd(数字)に当てはまるものがあれば
マッチする。
たとえば、11-aaa-22とか、22-33-44とかにマッチするということ。4.8.18 関数
ここでいう関数は正規表現に関連する関数ということ。
1) 正規表現に関する関数は5種類に分けられるか、どんなカテゴリか。
2) マッチングの関数は?
3) 置き換えの関数は?
4) 分割の関数は?
5) フィルタリングの関数は?
6) クォートの関数は?
7) Perl互換の正規表現とPerlの正規表現の違いは?
解答
1) マッチング・置き換え・分割・フィルタ・テキストのクォートの5つ。
2) preg_match() Perl形式のパターン。
preg_match_all()
3) preg_replace()
4) preg_split()
5) preg_grep()
6) preg_quote()
7) パターン文字列内でNull文字は扱えない
Perlの\E,\G,\L,\i,\Q,\u,\Uはサポートしていない
(?(Perlのコード))はサポートしていない
\D\G\U\u\A\x\
\v(垂直タブ)はサポートしている
先読み・戻り読み言明は*・+・?で繰り返せない
否定言明で使用した()付きのサブパターンにマッチした内容は記憶されない
戻り読み言明で選択肢を使う場合は、異なる長さの文字列を選択肢として選べる。4.8.18.1 マッチング
preg_matchについては結構使ってきているので割愛
FMTはこちらindex.phppreg_match_all(pattern,target_string,matches[,order]);target_stringをpatternで検索し、マッチしたすべての文字列をorderで指定した順番にmatchesで指定した配列に入れるというのが具体的な処理内容。
orderは2つのオプションが有る1) PREG_PATTERN_ORDER(デフォルト)
$matches[0] は 1 回目のマッチングでキャプチャした値の配列、 $matches[1] は 2 回目のマッチングでキャプチャした値の配列。
index.phppreg_match_all("|<[^>]+>(.*)</[^>]+>|U", "<b>example: </b><div align=left>this is a test</div>", $out, PREG_PATTERN_ORDER); echo $out[0][0] . ", " . $out[0][1] . "<br>"; echo $out[1][0] . ", " . $out[1][1] . "<br>";最初は「<」で、その次は「>」以外(※キャレットがあるから)のすべての文字が1文字以上で、次に「>」があり、次にどんな文字でも良いから1文字以上、次に</がきて、次はさっきのと同じで、>が来る。Uは貪欲さをなくすので、最小でマッチするものを探すということになる。
そんなわけで、\$match[0]は、<b>example: </b>と、<div align=left>this is a test</div>の2つ。
次にサブパターン、つまり(.*)の部分だから、example:と、this is a testの2つがくることになる。これはHTMLで抜き出すときに便利だな
2) PREG_SET_ORDER
$matches[0] は 1 回目のマッチングでキャプチャした値の配列、 $matches[1] は 2 回目のマッチングでキャプチャした値の配列、 といった順序。
index.phppreg_match_all("|<[^>]+>(.*)</[^>]+>|U", "<b>example: </b><div align=left>this is a test</div>", $out, PREG_SET_ORDER); echo $out[0][0] . ", " . $out[0][1] . "<br>"; echo $out[1][0] . ", " . $out[1][1] . "<br>";説明は上のとおりだが、配列の順番が変わる。
そんなわけで、\$match[0]は、<b>example: </b>と、example:が入り、
\$match[1]は<div align=left>this is a test</div>と、this is a testがくるつまり1)はタグのみの配列と、タグに囲まれた文字列のみの配列、
2)はマッチングしたタグとそのタグに囲まれた文字列を配列に入れている、ということになる。4.8.18.2 置換
replaceはWordの検索・置き換えと同じで、文字列中からパターンにマッチする内容を別の内容に変更する。
index.phppreg_replace (pattern , replacement , target[, limit, count])target に関して pattern を用いて検索を行い、 replacement に置換。
その回数はlimitになり、実際の置き換え回数がcountに入る。
limitのデフォルトは-1でこれはマッチしたものすべてに対して置き換えを行う。index.php$string = 'April 15, 2003'; $pattern = '/(\w+) (\d+), (\d+)/i'; $replacement = '${1}1,$3'; echo preg_replace($pattern, $replacement, $string);数字リテラルがあとに続く場合、\1の代わりに{1}を使うと正常に対応可能。
ちなみに上のパターンにはスペースが各()と()の間にあるので注意。
よって、{1}(\1)はApril,\2は15,\3は2003をキャプチャしている。
そこからリプレイスを行うと出力は「April1,2003」となる次はcount引数を使った場合
index.php$count = 0; echo preg_replace(array('/\d/', '/\s/'), '*', 'xp 4 to', -1 , $count); echo $count; //3出力は
xp***to
3まず パターンは 数字とスペースをに置き換えるということ。
結果としてxp**toになった。
最後は置き換えた回数が入るので、3になる。4.8.18.3 分割
・pret_split() 取り出す各部分を区切る文字が予めわかっている時はこれを使う
上にある記号をマッチした箇所で区切る。
FMTはpreg_split(pattern,string [,limit [,flags]]);index.php$ops = preg_split('{[+*/-]}','3+5*9/2'); var_dump($ops);この場合は+,*,/で区切るということなので、
array(4) { [0]=> string(1) "3" [1]=> string(1) "5" [2]=> string(1) "9" [3]=> string(1) "2" }
とオペランドが表示される。flagsは3つある
+ PREG_SPLIT_NO_ENTRY・・・区切り文字を返さない
+ PREG_SPLIT_DELIM_EMPTY・・・分断された部分が空文字ならリターンしない
+ PREG_SPLIT_DELIM_CAPTURE・・・パターンにマッチした箇所を返すindex.php$ops = preg_split('{([+*/-])}','3+5*9/2',-1,PREG_SPLIT_DELIM_CAPTURE); var_dump($ops);内容的にはかわらないが、パターンにマッチした箇所を返す、というので、出力は下記のようになっている。
array(7) { [0]=> string(1) "3" [1]=> string(1) "+" [2]=> string(1) "5" [3]=> string(1) "*" [4]=> string(1) "9" [5]=> string(1) "/" [6]=> string(1) "2" }index.php$array = preg_sprit('//',$string);これだと、文字列のすべての文字の間にマッチし、1文字ずつ抜き出す形になる。
4.8.18.4 フィルタリング
指定された配列の要素のうちパターンにマッチするものだけを返す。
下の例だと、末尾が.txtになるファイルだけを取得する。index.php$matching = preg_grep(pattern,array); //ex $textfiles = preg_grep('/.txt$/',$filenames);4.8.18.5 クォート
指定された文字列にのみマッチする正規表現を作成
index.php$re = preg_quote(string [, delimiter]); //ex echo preg_quote('$5.00 (five bucks)');こうすると、出力は
\$5\.00 \(five bucks\)になる
つまり特別な意味を持つ文字(*や$など)をstringで指定するとエスケープする。index.php$toFind = '/user/local/etc/rsync.conf/' $re =preg_quote($toFind, '/'); if (preg_match("/{$re}/",$filename)){ echo 'みつかりました!'; }上のようにオプション2番めのところにクォートを行う文字を指定すると、エスケープする。
- 投稿日:2021-03-29T16:03:56+09:00
Laravel6 チートシート
はじめに
Laravel触り始めたばかりの初心者です。
いちいち調べるのもめんどくさくなってきたので基本的な部分をまとめてみました。
自分用のチートシートにはなりますが、Laravel6で開発中の初心者の方も参考にしてください。
開発環境に関しては、MAMPで構築しています。Laravelのインストールから初期設定
Laravelのインストール
Laravelのインストールはcomposerコマンドで行います。
ターミナルcomposer create-project laravel/laravel (フォルダ名) --prefer-dist "6.0.*"
composer create-project
には2つのオプションがあります。
1. --prefer-dist
2. –prefer-source
--prefer-dist
は、zipファイルでダウンロードします、こっちのほうが高速なのでこちらを採用。
--prefer-source
は、git cloneでソースを落とします。Laravelの動作確認
Laravelのインストールが正常に行われたかを確認するために開発サーバーを起動します。
ターミナルphp artisan serve
初期設定
Laravelでやっておくべき初期設定は以下の7つです。
- タイムゾーン
- 言語設定
- DBの文字コード
- デバッグバー
- DB設定
- エラーメッセージの日本語訳
- HTMLにCSRFトークンの設置
タイムゾーン
タイムゾーンを日本時間に変更します。
config/app.php(70行目あたり)'timezone' => 'Asia/Tokyo'言語設定
言語設定を日本語に変更します。
config/app.php(83行目あたり)'locale' => 'ja'DBの文字コード
文字コードをUTF-8に変更します。
config/database.php(55行目~56行目あたり)'charset' => 'utf8', 'collation' => 'utf8_unicode_ci'デバッグバー
デバッグ用にデバッグバーを表示させます。
ターミナルcomposer require barryvdh/laravel-debugbar
現時点での設定では本番環境でもデバッグバーが表示されます。
DBなどの値がユーザーに見えてしまうため本番環境ではデバッグバーを非表示にする必要があります。.envAPP_DEBUG=falseDB設定
.envファイルのDBに関する記載の部分を修正していきます。
.envDB_CONNECTION=mysql //DB種類 DB_HOST=127.0.0.1 //ホスト DB_PORT=3306 //ポート DB_DATABASE=laravel //データベース名 DB_USERNAME=root //ユーザー名 DB_PASSWORD= //パスワード接続確認
ターミナルphp artisan migrate
エラー発生
修正すべきポイントは3つです。
①.envのDB_HOSTを修正
.envDB_HOST=localhost②config/database.phpのunix_socketを修正
config/database.php(54行目あたり)'unix_socket' => '/Applications/MAMP/tmp/mysql/mysql.sock'③ターミナルでキャッシュクリア
ターミナルphp artisan cache:clear php artisan config:cache
エラーメッセージの日本語訳
resources/lang/en
内にエラーメッセージのファイルがあるがこれらすべて英語となっています。
そのため、日本語訳するために以下のサイトからダウンロードします。
ダウンロードしたファイルを
resources/lang/ja
内に格納します。任意修正箇所
エラーメッセージが一部英語で表示されます。
エラーメッセージの変更は以下のように行います。
resources/lang/ja/validation.php"attributes" => [ "password" => "パスワード" ]HTMLにCSRFトークンの設置
こちらを入れておかないとフォームが必ずエラーとなります。
HTMLのheadタグ内の共通レイアウトbladeファイルを作り、そこに記載します。resources/views/layout.blade.php(レイアウトファイル)<meta name="csrf-token" content="{{ csrf_token() }}">フロントエンドでVue.jsを使うとき
Laravel6ではインストールが必要となりました。
ターミナル// laravel/uiをインストール composer require laravel/ui:^1.0 --dev // どちらかを選択 // ①auth機能あり php artisan ui vue --auth // ②auth機能なし php artisan ui vue // コンパイルとインストール npm install && npm run dev
npm run devでエラーが発生した場合
エラーメッセージは以下の通りです。
ERROR Failed to compile with 2 errors error in ./resources/sass/app.scss【原因】
sass-loaderのバージョンの互換性がない。
【対応】
バージョンの変更を行う
ターミナル// sass-loaderをアンインストール npm uninstall --save-dev sass-loader // sass-loaderをインストール npm install --save-dev sass-loader@7.1.0 // 実行 npm run dev
MVCアーキテクチャ
大きく分けて2つの動きが考えられます。
①DBの情報を必要としないとき
クライアント → ルーティング → コントローラー → ビュー②DBの情報を必要とするとき
クライアント → ルーティング → (コントローラー ⇄ モデル ⇄ DB) → ビュールーティング
フォルダ
routes/web.php
api
の場合は、routes/api.php
routes/web.php// ①直接Viewに移動 // Viewのwelcome Route::get("/", function() { return view("welcome"); } // ②Controllerに移動 // ControllerのUserControllerのindexメソッドに移動 Route::get("/user", "UserController@index");コントローラー
フォルダ
app/Http/Controllers
コントローラーの作成方法
ターミナルphp artisan make:controller UserController
リソースコントローラー(RESTful)
1行のコードで典型的なCRUDルートをコントローラーで作成できます。
ターミナルphp artisan make:controller PhotoController --resource
動詞 URI アクション ルート名 GET /tests index Tests.index GET /tests/create create Tests.create POST /tests store Tests.store GET /tests/{test} show Tests.show GET /tests/{test}/edit edit Tests.edit PUT/PATCH /tests/{test} update Tests.update PUT/PATCH /tests/{test} destroy Tests.destroy RestFulでよく使われる書き方
認証がされているときに表示する
routes/web.phpRoute::group(["prefix" => "photos", "middleware" => "auth"], function() { Route::get("index", "PhotosController@index")->name(Photos.index); Route::get("create", "PhotosConroller@create")->name(Photos.create); Route::posts("store", "PhotosController@store")->name(Photos.store); Route::get("show/{id}", "PhotosController@show")->name(Photos.show); Route::get("edit/{id}", "PhotosController@edit")->name(Photos.edit); Route::post("update/{id}", "PhotosController@update")->name(Photos.update); Route::post("destroy/{id}", "PhotosController@destroy")->name(Photos.destroy); })
id
の値をどのようにして取ればいいのか?resources/views/show.blade.php<form action="{{ route('test.edit', [ "id" => $test->id ]) }}" method="GET"> </form>値の取り出し方
値の取り出し方は2通りあります。
①Viewのフォーム経由から値を受け取る
②ModelでDBから値を受け取るView経由
app/Http/Controllers/UserControlleruse App\Models\User; public function store(Request $request) { // 値を取得する // $email = $request->input("email"); // $password = $request->input("password"); // 正しく取得できているか確認 // dd($email); // Modelを呼び出すだけでOK $user = new User; // インスタンス化したものに保存 $user->email = $request->input("email"); $user->password = $request->input("password"); // DBに保存 $user->save(); // リダイレクト return redirect("user/index"); }
POST
がうまくいかないとき
一旦キャッシュをすべてクリアしてみましょう。ターミナルphp artisan optimize:clear
Model経由
app/Http/Controller/UserController.php// ModelsのUserを読み込み use App\Models\User // クエリビルダーを使うためのおまじない use Illuminate\Support\Facades\DB // ①Eloquent // all() = すべてのデータを取得する $values = User::all(); // find() = 引数の値のidのデータを取得する $value = User::find($id); // where() = 条件指定(categoryカラムがPHPのデータを取得する) $value = User::where("category", "PHP")->get(); // ②クエリビルダー // get() = 条件にあったデータのすべてを取得する $values = DB::table("users")->get(); // first() = 最初の1件を取得する $value = DB::table("users")->first(); // SELECT = 取得するカラムを指定する $values = DB::table("users")->select("name", "email")->get(); // user.indexのViewに移動し、$valuesの値を渡す return view("user.index", compact("values"));
モデル
フォルダ
app/Models
モデルの作成方法
同時にマイグレーションファイルを作成すると便利
ターミナルphp artisan make:model Models/Post --migration
マイグレーションとは?
マイグレーションとは、DBテーブルの履歴を管理する仕組みです。
フォルダ
database/migrations
マイグレーションの頻出カラムタイプ
database/migrations/XXXX_XX_XXXXXX_create_users_table.phppublic function up() { Schema::create('registers', function (Blueprint $table) { // 符号なしBIGINTを使用した自動増分ID $table->bigIncrements('id')->comment('id'); // 20文字以内の文字列 $table->string('name', 20)->comment('名前'); // 255文字以内で独自の文字列 $table->string('email', 255)->unique('email')->comment('メールアドレス'); // 日付 $table->date('birthday')->comment('生年月日'); // 0~255までの整数値で符号なし $table->tinyInteger('gender')->unsigned()->comment('1:男性 2:女性'); // 最大65535文字で未記入可 $table->text('memo', 65535)->nullable()->comment('メッセージ'); $table->timestamps(); }); }テーブルにカラムを作成する
ターミナルphp artisan migrate
マイグレーションを修正する
①migrationファイルを新規に作成する
--table
オプションでテーブル名を指定します。
ファイル名は何をするかをわかりやすくするのがおすすめです。
下記の場合、「usersテーブルにemailを追加する」です。ターミナルphp artisan make:migration add_email_users_table --table=users
②migrationファイルを修正
database/migrations/XXXX_XX_XXXXXX_add_email_users_table.phppublic function up() { Schema::table('users', function (Blueprint $table) { $table->string('email'); //カラム追加 }); } public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('email'); //カラムの削除 }); }③migration
ターミナルphp artisan migrate
ビュー
フォルダ
resources/views
assetsヘルパー
Bladeファイル内にCSSやJSを記述すると可読性が著しく落ちるので別ファイルに保存します。
public |__ css |__ js |__ imgresources/views/index.blade.php<!-- css --> <link rel="stylesheet" href="{{ asset('/css/style.css') }}"> <!-- js --> <script src="{{ asset('/js/index.js') }}"></script> <!-- img --> <img alt="ロゴ" src="{{ asset('/img/logo.jpg') }}">ダミーデータ
Factory & Faker
フォルダ
database/factories
ターミナルphp artisan make:factory PostFactory
ここで修正が必要です
database/factories/PostFactory.php// これは誤り // use App\Model // こちらが正しい use App\Models\Post // ModelではなくPostのためこちらも誤り /* $factory->difine(Model::class, function (Faker $faker) { return [ ]; } */ //こちらが正しい $factory->define(Post::class, function (Faker $faker) { return [ "name" => $faker->name, "title" => $faker->realText(50), "email" => $faker->unique()->email, "tel" => $faker->phoneNumber, "password" => $faker->password, "url" => $faker->url, "gender" => $faker->randomElement(["0", "1"]), "age" => $faker->numberBetween($min = 1, $max = 6), "contact" => $faker->realText(200) ]; });その他のFakerに関しては以下を確認してください。
作成手順
①config/app.phpの修正
config/app.php(109行目あたり)"faker_locale" => "ja_JP"②シーダの作成
ターミナルphp artisan make:seeder PostSeeder
PostSeeder.phpuse App\Models\Post public function run() { // 200個作成 factory(Post::class, 200)->create(); }③データベースシーダーに追記
database/seeds/DatabaseSeeder.phppublic function run() { $this->call([ // ここに付け足していく UsersTableSeeder::class, PostsSeeder::class, ]); }④ターミナル
ターミナル// Composerのオートローダーを再生成する composer dump-autoload // データベースに入っている値を初期化し入力する場合 php artisan migrate:fresh --seed
バリデーション
フォルダ
app/Http/Requests
ターミナルphp artisan make:request StoreBlogPost
app/Http/Requests/StoreBlogPost.php// ①trueに変更(これをしないとうまく作用しない) public function authorize() { return true; } public function rules() { return [ // 【必須】最大20文字 "name" => "required|string|max:20", // 【必須】最大255文字のメールアドレスであり被りがあってはならない "email" => "required|email|unique:users|max:255", // 【必須】8文字以上で確認用と同じパスワード "password" => "required|confirmed|min:8", // 【任意】 "hobby" => "nullable", ] }app/Http/Controllers/StoreBlogPostController// ファイルを読み込む use App\Http\Requests\StoreBlogPost; // これで自動的にバリデーションができるようになる public function store(StoreBlogPost $request) { }resources/views/blogpost.blade.php// ①それぞれ書く @error("email") <span> <strong>{{ $message }}</strong> </span> @enderror // ②まとめて書く @if ($errors->any()) <div> <ul> @foreach($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif
- 投稿日:2021-03-29T15:37:55+09:00
PHP fopenでファイルを開いて使用する
fopenの例
読み込み・追加の処理
txt.txt
abcdefg one,two,three 12345 アイウエオfopen.php
<?php //$変数名(ファイルポインタ) = fopen (“開きたいファイル名”, “オープンモード”); // 読み込みモードでファイルを開く $fp = fopen("txt.txt", "r"); // ファイルを1行ずつ取得する while ($line = fgets($fp)) { echo "$line"; } // ファイルを閉じる fclose($fp); //結果 /* abcdefg one,two,three 12345 アイウエオ */ //追加の記述(末尾に追加される) $fa = fopen('txt.txt','a'); fwrite($fa,"\n".'5行目に追加'); fclose($fa); $fr = fopen('txt.txt','r'); while($line = fgets($fr)){ echo $line; } /* //結果 /* abcdefg one,two,three 12345 アイウエオ 5行目に追加 */上書きの処理
さらにこの記述の後に以下のコードを実行すると上書きされる。
$fw = fopen('txt.txt','w'); fwrite($fw,'上書きして1行になります'); fclose($fw); $fr = fopen('txt.txt','r'); while($line = fgets($fr)){ echo $line; } fclose($fr); //結果 //上書きして1行になりますfopen()の第二引数について
以下の用途で指定する
参考:phpドキュメント
- 投稿日:2021-03-29T14:26:51+09:00
PHP 関数は同一ファイル内であれば宣言より前に記述しても呼び出し可能
- 投稿日:2021-03-29T14:26:51+09:00
PHP 関数は同一ファイル内であれば宣言より前に記述しても呼び出し可能。呼び出しは大文字と小文字を区別しない。
関数の呼び出しについてのメモ
例)
<?php <?php echo "上です\n"; hoge(); function hoge(){ echo "hogeですファイル内から呼べます\n"; echo "関数は同一ファイルであれば宣言より前に記述しても呼べます\n"; } echo "下です\n"; hoge(); echo "関数は大文字と小文字を区別しない\n"; HOGE(); echo "小文字と大文字を混ぜても平気\n"; hoGe(); /* 上です hogeですファイル内から呼べます 関数は同一ファイルであれば宣言より前に記述しても呼べます 下です hogeですファイル内から呼べます 関数は同一ファイルであれば宣言より前に記述しても呼べます 関数は大文字と小文字を区別しない hogeですファイル内から呼べます 関数は同一ファイルであれば宣言より前に記述しても呼べます 小文字と大文字を混ぜても平気 hogeですファイル内から呼べます 関数は同一ファイルであれば宣言より前に記述しても呼べます */宣言の前後で関数が呼び出せていることを確認できる。
- 投稿日:2021-03-29T14:18:30+09:00
PHP unset(), implode(), explode(), in_arrayの使い方
各関数の意味
unset
変数の割り当てを解除する
implode
配列を引数に指定した文字で区切り文字列として結合する
explode
文字列を指定した値を区切りにして配列に分割する
in_array
配列の中から指定した値があるか確認する
例)
<?php //unset $foo = 'test'; echo $foo."\n"; unset($foo);// 変数を削除する echo 'hoge'."\n"; echo $foo."unset\n"; echo 'hoge2'."\n"; /* test hoge unset //unset()によってtestunsetとならず「unset」のみがechoされている hoge2 */ //implode $arr = ["one","two","three"]; echo implode($arr)."\n";// onetwothre ←第一引数に分割する値を入れないと配列を単純に結合する。 echo implode(",", $arr)."\n";// one,two,three ←引数を指定した事で第一引数の「,」を配列のインデックスごとに挿入して結合している //explode $str = "one,two,three"; // one,two,three var_dump(explode(",", $str)); /* array(3) { [0]=> string(3) "one" [1]=> string(3) "two" [2]=> string(5) */ //↑第一引数で指定した「,」で文字列を分割し配列を作っている $search_arr = ['remon','stroberry','apple']; if(in_array('apple',$search_arr)){ echo 'リンゴがあります'; }else{ echo 'リンゴはありません'; } //リンゴがあります //第一引数で指定した値が配列内にあればtrueを返す(なければfalseを返す)。
- 投稿日:2021-03-29T03:03:45+09:00
ボクとPHP・Laravel・Dockerとの約200時間
始まりは突然に…
あれは、ある企業に面接したときの事でした。それまで、私はRubyでの開発を行っており、ポートフォリオもRailsで作成していました。その面接で、ポートフォリオの説明を行い面接終了時に次のようなお言葉を頂いた時から始まりました。
あなたに課題を出します。それを期限を決めませんが行ってください。とのことでした。課題とは以下の通りです。
それを成し遂げた理由
![]()
それは、至って単純な話です。
やってみたい・挑戦してみたいと思ったかです!
やりたいやってみせるという気持ちで望みました!で、一体何から着手したのか
プロセスは以下の通りです。
①プロゲートの初級(PHP基礎学習)
↓
②ドットインストールPHPに関するもの全て(PHP基礎学習)
↓
③ドットインストールDockerに関するもの全て(Docker基礎学習)
↓
④ドットインストールLaravel基礎の部分のみ(Laravel基礎学習)
↓
⑤Docker環境下でLaravel立ち上げYouTube等の動画を元に実践(Laravel実践学習)
↓
⑥ちょっとした掲示板を作成(Laravel実践学習)
↓
⑦ポートフォリオ作成(ErrorStocker作成)
この様な流れで実施
以下が着手時間です。学習管理に関しては、Studyplusを利用しました。
・PHP基礎学習に費やした時間:31時間11分(2月6日〜2月11日)
・Docker基礎学習に費やした時間:8時間10分(2月7・11・12日)
・Laravel基礎学習に費やした時間:31時間24分(2月11〜18日)
・ErrorStocker作成に費やした時間:130時間4分(2月18日〜3月18日)
合計時間:約200時間?作成したポートフォリオ
題名:ErrorStocker
URL:http://error-st.com
GitHub:https://github.com/miyaseinto/ErrorStokcer何を目的に作成したのか??の前に軸を話させてください!
少し私の話になりますが少々お付き合いください!(どうでもいいんだよテメェの話なんかと思った人は飛ばしてください笑)
これは、私がエンジニアを目指した軸にあります。
【不便に感じたものをデジタル化でより楽ができるように便利に変えたい】これが私がエンジニアを目指した軸です。
なぜその様な軸ができたかというと、前職での公務員経験からこの様な考えが生じたからです。
前職の公務員は超田舎で勤務していました。そのため、デジタル化が都会より著しく劣っており何をするにもアナログで不効率でした。何より住民の方が大変な思いをすることが多々ありました。回覧物だったり、各種手続きだったり、仕方がないことだと考えていました。ですが、エンジニアという職業がそれらを解決に導く職業であるのでは無いかと調べるうちに、それを担ってやりたいという思いが日に日に増していきました。
で、それを勉強したいと思い勉強していく中で自分が書いたコードがブラウザ画面で変化していくことにとてもやりがいを感じ、これを仕事にしたいと考え公務員を退職しエンジニアを目指そうと思い勉強に励みました。
退職した職場には申し訳なかったのですが、自分の腹の中を上司の方々に話すとそれならば挑戦してみろと背中を強く押してくれました。ですので、その思いを全部背負っとるじゃいと思いながらエンジニアになりたいと考えてます。これがエンジニアを目指した軸のお話です。(長々と申し訳ないです笑)作成背景&目的
上記の軸を基に、プログラミングの勉強をしている時に不便に感じたことを便利に変えるために作成しました。
私が、エラーと遭遇した時にリファレンスサイトやQiitaの内容からエラー解決に導こうとします。そこで、いつもは一度エラーした内容をGoogleのブックマークの中にファイルごとに保存して、もう一度確認をしたい時に見直しをしておりました。しかし、情けない話そのファイルをどこに保存したのかがわからなくなり、探すことに時間を掛けることがありました。そこで、検索をかけてその内容を短時間で探せることはできないかと考え作成に至りました。工夫した・苦労したPOINT
・投稿内容をマークダウンで投稿できるようにしたこと(リンクを文字列内に入れたいと考えたため)
・上記と同じだが、コメント機能にもマークダウンを使用したこと(上記と同じ)
・投稿した時間を表記させたこと
・コメントした時間を表記させたこと
・Bootstrapを使用したが、Bootstrap感を排除したこと
・写真の圧縮を行ったこと
・タグを一覧でも表示させたこと
・閲覧用としてログイン簡略化させたこと(通常のログインと表示は異なる)
・ページネーションを導入したこと
・キーワード検索をタイトルの内容と本文の内容で検索できるようにしたこと
・キーワード検索で検索件数を表示させたこと?機能一覧
ユーザー機能
・ユーザー登録(投稿用ログイン)
・ゲストログイン(閲覧用ログイン)
・マイページにて以下の投稿の一覧表示
・自分の投稿内容
投稿機能
・エラーのストックをログインアカウントが投稿・編集・削除
・一覧表示、詳細表示
・投稿一覧表示で10個の投稿数をページネーションを実施
・写真投稿及び圧縮(intervention/image)
・タグ付け(タグ検索)
・キーワード検索(タイトル・内容)
・投稿内容にマークダウンを採用(cebe/markdown)
コメント機能
・投稿にコメントを投稿・編集・削除
・投稿詳細ページにコメント一覧表示
・コメント内容にマークダウンを採用(cebe/markdown)?使用技術
フロントエンド
・HTML / CSS / Bootstrap
バックエンド
・PHP 8.0.2
・Laravel 8.28.1
データベース
・Mysql 8.0
開発環境
・Docker 20.10.2
・docker-compose 1.27.4
本番環境
・AWS(VPC、EC2、S3、Route53)
・Nginx
各種解説内容投稿
- 投稿日:2021-03-29T03:03:28+09:00
ポートフォリオの解説投稿(自分なりのアウトプットです!!)
自分が書いたコードを説明できないのは恥だよ!!
これは、あるYouTuberの言葉です。
確かにそうですよね…
はい、やります。やらせてください!
解説やらせてください!
てな感じで始めます笑
と、その前にこちらがポートフォリオの内容です。三部構成でいきます!
Controller・Model・Viewの三部構成で解説していきます。
主にTweetの内容からです。まずControllerから行きます!
app/Http/Controllers/TweetController.phpnamespace App\Http\Controllers; #namespaceとは、名前空間といい、こちらを使用してクラス被りをしないようにする。というもの。 use App\Models\Tweet; use App\Models\Tag; use App\Models\Comment; #各種のModelを読み込む際の記述 use Storage; use Illuminate\Support\Str; #ストレージファイルの読み込む際の記述 use InterventionImage; use Image; #写真の編集ライブラリ(Intervention Image)を使用したため、その読み込みの記述 use Illuminate\Http\Request; use App\Http\Requests\TweetRequest; #Requestファイル(バリデーションのルールが書いてあるファイル)を読み込む際の記述 class TweetController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { $q = \Request::query(); #$qにリクエストのクエリストリングを入れる。 if(isset($q['tag_name'])){#もし、$qの中のtag_nameに値があればtrueを実行する $tweets = Tweet::with(['user', 'tags'])->latest()->where('tag_box', 'like', "%{$q['tag_name']}%")->paginate(10); #$tweetsにN+1問題を解決するためにwithを使用し[userとtags]のModel情報を取得し、最新の情報を取得し、カラム名tag_boxを文字列検索で$q['tag_name']を取得し、ページネーションで10ページを表示させる。 #withとはリレーションを解決したい時に使用する。 #whereの引数について、第1引数はカラム名です。第2引数はデータベースがサポートしているオペレーターです。第3引数はカラムに対して比較する値である。 $tags = \DB::table('tags')->get(); #$tagsにtagsテーブルの情報を取得する。 return view('tweets.index', [ 'tweets' => $tweets, 'tags' => $tags, 'tag_name' => $q['tag_name'] ]); #返り値でViewに値を渡す。 }else { $tweets = Tweet::with(['user', 'tags'])->latest()->paginate(10); $tags = \DB::table('tags')->get(); $tags_name = []; foreach ($tags as $tag) { array_push($tags_name, $tag->tag_name); } #tags_nameに配列として値を渡す。foreachで値を回し、タグテーブルの情報からtag_nameを配列に追加していく。 return view('tweets.index', [ 'tweets' => $tweets, 'tags' => $tags ]); } } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { return view('tweets.create' ); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(TweetRequest $request) { $tweet = new Tweet; $tweet->user_id = $request->user_id; $tweet->content = $request->content; $tweet->tag_box = $request->tag_box; $tweet->title = $request->title; #$tweetにパラメーターで取得した情報をそれぞれ値を入れていく。 if($request->hasFile('image')){#パラメーターの中にimageが入っていたらtrueで値を返す。 $filename = $request->file('image');#リクエストでimageファイルとして$filenameに入れる。 $name = $filename->getClientOriginalName(); #$filenameの画像の名前を取得する $ext = strtolower(substr($filename->getClientOriginalName(), strrpos($filename->getClientOriginalName(), '.')+1));#strtolowerで大文字を小文字に戻し、substrで文字を切り出す。これで、写真の拡張子を取得する。 if(!in_array($ext, ['png', 'jpg', 'gif', 'jpeg'], true)) {#['png', 'jpg', 'gif', 'jpeg']の配列に$extの値があるかチェックする。 $tag_view = '画像以外のファイルが指定されています。画像ファイル(png/jpg/jpeg/gif)を指定して下さい'; return view('tweets.tag', compact('tag_view')); #tweets.tagのViewにcompactでtag_viewの情報を渡して表示させる。 } $imageFile = time(). '_' . $name; $imagePath = storage_path('app/public/') . $imageFile; $image = Image::make($filename)#imageを作成する ->resize(1000, null, function ($constraint) { $constraint->aspectRatio();#横幅を1000にして縦横比を保持したまま変更を行う。 $constraint->upsize();#小さい写真を無理やり1000にすることをせずにそのままのサイズを維持する。 }) ->orientate()#画像の向きを自動的に調整する。 ->save($imagePath);#その情報を一旦ローカルに保存する。 $path = Storage::disk('s3')->putFile('myprefix',$imagePath, 'public');#config/filesystems.phpの中に設定した情報からawsのs3にファイル名は自動で生成し保存する。 $tweet->image = Storage::disk('s3')->url($path); Storage::disk('local')->delete('app/public/' . $imageFile);#ローカルの情報を削除する。 } preg_match_all('/#([a-zA-Z0-90-9ぁ-んァ-ヶー一-龠]+)/u', $request->tag_box, $match);#ハッシュタグで始まる単語を取得。結果は、$matchに多次元配列で代入される。 # $match[0]に#(ハッシュタグ)あり、$match[1]に#(ハッシュタグ)なしの結果が入ってくるので、$match[1]で#(ハッシュタグ)なしの結果のみを使う。 $tags = []; foreach ($match[1] as $tag) { $found = Tag::firstOrCreate(['tag_name' => $tag]);#firstOrCreateメソッドで、tags_tableのnameカラムに該当のない$tagは新規登録される。 array_push($tags, $found);#$foundを配列に追加します(=$tags) } #投稿に紐付けされるタグのidを配列化 $tag_ids = []; foreach ($tags as $tag) { array_push($tag_ids, $tag['id']); } $tag_count = count($tag_ids);#tagの数を取得する。 if ($tag_count <= 5){#もしtagの数が5以下ならtrueを実施 $tweet->save(); $tweet->tags()->attach($tag_ids);#投稿ににタグ付するために、attachメソッドをつかい、モデルを結びつけている中間テーブルにレコードを挿入 return redirect('/top'); } else{ $tag_view = 'タグ数が5つ以上ですので変更してください。'; $tweet_id = $id; return view('tweets.tag-edit', compact('tag_view','tweet_id')); } } /** * Display the specified resource. * * @param \App\Models\Tweet $tweet * @return \Illuminate\Http\Response */ public function show(Tweet $tweet) { $tweetid = $tweet->id; $comments = Comment::where('tweet_id', '=', $tweetid)->get();#commentの情報をtweet_idと$tweetidの情報が同じならその情報を取得する。 return view('tweets.show',[ 'tweet' => $tweet, 'comments' => $comments, ]); } /** * Show the form for editing the specified resource. * * @param \App\Models\Tweet $tweet * @return \Illuminate\Http\Response */ public function edit($id) { $tweet = Tweet::with(['user','comments'])->findOrFail($id);#[userとcomments]のModel情報を取得し、$id(パラメーター)の情報から取得する。 return view('tweets.edit',[ 'tweet' => $tweet, ]); } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param \App\Models\Tweet $tweet * @return \Illuminate\Http\Response */ public function update(Request $request) { #ここはstoreクラスとほとんど同じ $id = $request->tweet_id; $tweet = Tweet::findOrFail($id); $tweet->content = $request->content; $tweet->title = $request->title; $tweet->user_id = $request->user_id; $tweet->tag_box = $request->tag_box; if($request->hasFile('image')){ $filename = $request->file('image'); $name = $filename->getClientOriginalName(); $ext = strtolower(substr($filename->getClientOriginalName(), strrpos($filename->getClientOriginalName(), '.')+1)); if(!in_array($ext, ['png', 'jpg', 'gif', 'jpeg'], true)) { $tag_view = '画像以外のファイルが指定されています。画像ファイル(png/jpg/jpeg/gif)を指定して下さい'; return view('tweets.tag', compact('tag_view')); } $imageFile = time(). '_' . $name; $imagePath = storage_path('app/public/') . $imageFile; $image = Image::make($filename) ->resize(1000, null, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }) ->orientate() ->save($imagePath); $path = Storage::disk('s3')->putFile('myprefix',$imagePath, 'public'); $tweet->image = Storage::disk('s3')->url($path); Storage::disk('local')->delete('app/public/' . $imageFile); } preg_match_all('/#([a-zA-Z0-90-9ぁ-んァ-ヶー一-龠]+)/u', $request->tag_box, $match); $tags = []; foreach ($match[1] as $tag) { $found = Tag::firstOrCreate(['tag_name' => $tag]); array_push($tags, $found); } $tag_ids = []; foreach ($tags as $tag) { array_push($tag_ids, $tag['id']); } $tag_count = count($tag_ids); if ($tag_count <= 5){ $tweet->save(); $tweet->tags()->sync($tag_ids); return redirect('/top'); } else{ $tag_view = 'タグ数が5つ以上ですので変更してください。'; $tweet_id = $id; return view('tweets.tag-edit', compact('tag_view','tweet_id')); } } /** * Remove the specified resource from storage. * * @param \App\Models\Tweet $tweet * @return \Illuminate\Http\Response */ public function destroy($id) { #Tweetモデルから$idを見つけ出す。 $tweet = TWeet::find($id); preg_match_all('/#([a-zA-Z0-90-9ぁ-んァ-ヶー一-龠]+)/u', $tweet->tag_box, $match); $tags = []; foreach ($match[1] as $tag) { $found = Tag::firstOrCreate(['tag_name' => $tag]); array_push($tags, $found); } $tag_ids = []; foreach ($tags as $tag) { array_push($tag_ids, $tag['id']); } $tweet->tags()->delete($tag_ids);#deleteで削除する。 $tweet->delete();#上記と同じ return redirect('/top'); } public function search(Request $request) { $tweets = Tweet::where('title' ,'like', "%{$request->search}%") ->orwhere('content' ,'like', "%{$request->search}%") ->paginate(10);#Tweetモデルから情報を取得し、where句を使用してパラメータで取得した値をtitleカラムとcontentカラムの中に同じ値があるかを取得する。でそのページネーションを10で表示させる。ものを$tweetsに入れる。 $search_result = '【'. $request->search. '】の検索結果は'.$tweets->total().'件';#検索情報と検索にヒットした数を$search_resultに入れる。 $tags = \DB::table('tags')->get(); return view('tweets.index',[ 'tweets' => $tweets, 'search_result' => $search_result, 'search_query' => $request->search, 'tags' => $tags, ]); } }次はModelの解説していきます!
app/Models/Tweet.phpnamespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Tweet extends Model { protected $table = 'tweets'; protected $fillable = [ 'title', 'user_id','tag_box','content', 'image', ]; public function user(){ return $this->belongsTo(\App\Models\User::class,'user_id'); }#tweetとuserとの1対多の関係 public function tags(){ return $this->belongsToMany('App\Models\Tag'); }#tweetとtagとの多対多の関係 public function comments(){ return $this->hasMany(\App\Models\Comment::class,'tweet_id', 'id'); }#tweetとcommentとの1対多の関係 }最後にViewの解説をします!
resources/views/tweets/index.blade.php@extends('layouts.app')#部分テンプレート @section('content') <h2 class="card-header" style="text-align: center;"> 投稿一覧 </h2> @isset($search_result)#$search_resultの情報が入っていればtrueで実施 <h5 class="card-title" style="text-align: center; padding-top: 30px; font-size: 20px; color: #55c500">{{ $search_result }}</h5> @endisset <div class="row m-3"> <div class="col-sm-3"> <h5 class="card-title"><i class="fas fa-tags"></i>タグ一覧</h5> @foreach($tags as $tag)#Controllerから$tagsの情報を取得して$tagに入れて回す。 <a href="{{ route('tweets.index', ['tag_name' => $tag->tag_name]) }}" class="btn btn-outline-success m-1"> {{ $tag->tag_name }}#tag_nameを表示させる </a> @endforeach </div> <div class="col-sm-9"> @if (session('status')) <div class="alert alert-success" role="alert"> {{ session('status') }} </div> @endif @foreach ($tweets as $tweet) <div class="toast fade show" role="alert" aria-live="assertive" aria-atomic="true"> <div class="toast-header"> <strong class="mr-auto"> <a href="{{ route('users.show', $tweet->user_id) }}" class="btn btn-outline-primary btn-sm">{{ "@".$tweet->user->name }}</a> が <span class="text-muted" style="font-size:15px;">{{ $tweet->created_at->format('Y年m月d日') }}にストック</span> </strong> </div> <div class="toast-body"> <h5 class="card-title"> <i class="fas fa-tags"></i> @foreach($tweet->tags as $tag) <a href="{{ route('tweets.index', ['tag_name' => $tag->tag_name]) }}" class="badge badge-success"> #{{ $tag->tag_name }} </a> @endforeach </h5> <a href="{{ route('tweets.show', $tweet->id) }}" class="text-dark"> <h2 class="card-title">{{ $tweet->title }}</h2> </a> </div> </div> @endforeach @if(isset($tag_name))#もし$tag_nameに情報があればtrueで実施 {{ $tweets->appends(['tag_name' => $tag_name])->links() }}#tag_nameを基にペジネーションリンクにクエリ文字列を付け加えたいときは、appendsメソッドを使用する。linksメソッドは結果の残りのページヘのリンクをレンダーする。 @elseif(isset($search_query))#もし$search_queryの値が入っていればtrueを実施 {{ $tweets->appends(['search' => $search_query])->links() }} @else {{ $tweets->links() }} @endif </div> </div> @endsectionとりあえず、これでアウトプットとします。
自分なりのアウトプットですので間違い等はあるかもしれませんが、自分のための投稿しています。
- 投稿日:2021-03-29T02:17:36+09:00
パスからidを取得したい
背景
dreddでapiテストを作成している。
テストデータを作成するときに使用するidを取得したい。
そのためにパスからidを取得する。環境
PHP 7.3.22
末尾のidを取得する
$path = '/users/30'; $id = intval(basename($path)); var_dump($id);結果
int(30)やっていること
basenaemでパスの末尾の文字列を取得する
intvalで整数型に変換するidがパスの途中にある場合
idが末尾になるようにパスから末尾の文字列をトリムすればいい
$path = '/users/30/admin'; $id = intval(basename(rtrim($path, '/admin'))); var_dump($id);結果
int(30)ちょっと沼ったのでメモ
basename()の第2引数にsuffixを指定できる。
これを指定することで、指定した文字列を除いて末尾の文字列を取得できる。
参考
https://www.php.net/manual/ja/function.basename.phpそこで最初は以下のようにしていた
$path = '/users/30/admin'; $id = intval(basename($path, '/admin')); var_dump($id);その結果は以下のようになった。
int(0)問題点
- basenameのsuffixは'/'で区切った最末尾の文字列('admin')から'/admin'がその末尾にあれば、除くことになるので、この場合'admin'がbasenameした結果になる
- intvalは失敗するとint(0)を返す
その結果、idが0のテストデータを作成していたため、テスト結果がエラーも出さず望んだ結果にならずに沼ってしまった。
- 投稿日:2021-03-29T00:09:54+09:00
【PHP】GET・POST サーバー 作成
今回はphpのget・postサーバーの作成について書きます。
正直、今回の記事は自身のメモとしての意味合いが大きいです。
似たようなことが書かれた記事は多いですし、コードの内容も多くないため簡潔に書いていこうと思います。実装環境
今回はそんなに手間をかけるつもりはないため、xamppを使用します。
VSCodeを使用してコードを書きましたが、コマンドでビルドが必要だとかいうこともないので、テキストエディターは何でも良いです。GET・POST サーバー
index.php<?php if (isset($_POST['post_sample'])) { echo $_POST['post_sample']; } if (isset($_GET['get_sample'])) { echo $_GET['get_sample']; }PHP側のコードはこれだけです。
$_POST['~']
でPOSTで送られてきた値を取得し、$_GET['~']
でGETで送られてきたデータを取得します。クライアント側(html)
動作確認のために次はクライアント側のhtmlのコードを作成します。
index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UFT-8"> <title>get_post_sample</title> </head> <body> <form action="index.php" method="GET"> <input type="text" name="get_sample" value="get_sample_text"> <input type="submit" value="get"> </form> <form action="index.php" method="POST"> <input type="text" name="post_sample" value="post_sample_text"> <input type="submit" value="post"> </form> </body> </html>細かい説明は省きますが、formのinputを使用することでGET・POSTのいずれも送信することができます。(見てわかるかもしれませんが、methodの部分でGETとPOSTを定義してます)
動作確認
xamppを起動してページを呼び出せば下記の様なページになります。
ボタンのget
を押せばGETで送信され、ボタンのpost
を押せばPOSTで送信されます。GET送信時
php側の処理はecho
しているだけなので、画面は送信したget_sample_textだけが表示されます。URLを確認してもらえばわかるかともいますが、GETの挙動としてパラメータがURLに表示されています。POST送信時
php側で設定してる内容はGETとほぼ同じなので、挙動に大きな差は見られません。
POST時の挙動として、GETと違いパラメータがURLには表示されません。クライアント側(JavaScript)
今回は、JavaScriptでもクライアント側を作成してみます。
script.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UFT-8"> <title>get_post_sample</title> </head> <body> <script> function do_post() { let xhr = new XMLHttpRequest(); xhr.open('POST', 'index.php', true); xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;charset=UTF-8'); let request = "post_sample=post_text_by_js"; xhr.send(request); //post送信後のレスポンス xhr.onreadystatechange = function () { alert(xhr.responseText); } } </script> <input type="button" value="post" onclick="do_post()"> </body> </html>処理としては
XMLHttpRequest
オブジェクトを生成してPOSTを送信(.send()
)します。
そのあと、onreadystatechange
に設定された内容の通りに、PHP側から送られてきたPOSTのレスポンスを処理します。動作確認
作成したページを開き、postボタンを押下するとonreadystatechange
で処理を設定した通り、アラートが表示されます。
今回の場合は、アラートで表示される内容はphpのページで表示される内容と同じなので、postで送信した内容がそのまま表示されることになります。(php側のソースで、POSTで送られてきた内容をそのままechoしているだけになっているため)
htmlで作成した場合と違い、画面遷移することはありません。終わりに
今回はGET・POSTのサーバー側と、クライアント側を二種類書きました。
簡潔に書くと言った割に長いような…
PHPはあまり触ってないので、個人的にはもう少し腕を磨きたいと思っています。
あと、GET・POSTというとJavaのサーブレットを仕事でやったことがありましたが、結構前のことで記憶が怪しいので、時間があればまとめようかと思います。