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

ページごとに別のスタイルシートを読み込みたい

やりたいこと ページごとに違うスタイルシートを読み込む。 htmlからPHPにした際に、ヘッダーはheader.phpにまとめたので、動的に読み込むスタイルシートを変更させたい。 index.php のとき style.css mypage.php のとき mypage.css を読み込むようにする。 変数を使ってそれぞれにシート名を代入する PHPの変数を使って動的に読み込む。 header.php <link rel="stylesheet" href="../css/<?= $css_file ?>.css"> index.php <?php $css_file = 'style'; include ('header.php'); ?> <!-- html --> mypage.php <?php $css_file = 'mypage'; include ('header.php'); ?> <!-- html --> これでページごとに違うスタイルシートを読み込むことができた。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel-Mix】npm run productionしたらエラーが出た時の苦し紛れの解決方法

npm run productionしたらエラーが出た Terminal npm run production 前提 Laravel 8.4 webpack-cli 4.9 Laravel-mix 5.1 Laravel-uiのLaravel-mixを使いたかった。 package.json "scripts": { "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" } 苦し紛れの解決方法 そうだ、Laravel-mixを最新にしよう Terminal npm install laravel-mix@latest 私の環境ではうまくいきました。 Terminal npm run production Laravel Mix v6.0.39 ✔ Compiled Successfully in 10197ms ちなみに公式Docによるとlaravel-mixバージョン6以上の場合、 package.json "scripts": { "development": "mix", "watch": "mix watch", "watch-poll": "mix watch -- --watch-options-poll=1000", "hot": "mix watch --hot", "production": "mix --production" } scriptsがこんなに綺麗になる。エラーも出ない。すごい(小並感) 以下、解決するまで試したこと エラー文1 Terminal [webpack-cli] Error: Unknown option '--no-progress' [webpack-cli] Run 'webpack --help' to see available commands and options ⇒ package.jsonのscriptsから --no-progressを削除し、再実行 エラー文2 Terminal [webpack-cli] Error: Unknown option '--hide-modules' [webpack-cli] Run 'webpack --help' to see available commands and options ⇒ package.jsonのscriptsから --hide-modulesを削除し、再実行 --no-progressと--hide-modulesを消したscripts package.json "scripts": { "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --config=node_modules/laravel-mix/setup/webpack.config.js" } エラー文3 Terminal [webpack-cli] Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema. - configuration.module.rules[10] has an unknown property 'loaders'. ⇒ webpack.config.jsを確認したところ、'loaders'プロパティが見当たらない。 npm-moduleを削除し、npm installする Terminal rm node_modules Terminal npm install その後も再びエラー文3が出る。 おわり バージョンの選択はやはり塩梅が難しい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

備忘録~詰まってしまった繰り返し処理においての疑問点

<?php $scores = []; do{ echo '点数を入力 >'; $score = (int)trim(fgets(STDIN)); if ($score === -1){ break; } $scores[] = $score; $sum = 0; for ($i = 0; $i < count($scores); $i++) { echo $i + 1 . ':' . $scores[$i] . PHP_EOL; $sum += $scores[$i]; } echo '合計:' . $sum . PHP_EOL; } while(true); /* 80=> 点数を入力 >80 1:80 合計:80 80=> 1:80 2:80 合計:160 点数を入力 > */ 上コードは入力した値とその合計を出力する。 <?php $scores = []; $sum = 0; do{ echo '点数を入力 >'; $score = (int)trim(fgets(STDIN)); if ($score === -1){ break; } $scores[] = $score; for ($i = 0; $i < count($scores); $i++) { echo $i + 1 . ':' . $scores[$i] . PHP_EOL; $sum += $scores[$i]; } echo '合計:' . $sum . PHP_EOL; } while(true); /* 80=> 点数を入力 >80 1:80 合計:80 80=> 1:80 2:80 合計:240 点数を入力 > */ $sum = 0;の場所を変えるとうまく動作しなくなった。 => scoresには今まで入力した値が格納されており、合計(sum)はscoresの配列の値を足し合わせている。 つまり、sumは繰り返し処理の際に毎回0にしないと値が重複してしまう。 ※①80を入力 scores =[80]; 合計:80 ②80を入力 scores =[80,80]; 合計:80(前に残った値)+80+80=240 へんに詰まってしまった...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(備忘録)初学者からLaravelで開発できるまでの要点まとめ

まずは軽くLaravel内でのクラス、メソッド、プロパティの書き方をおさらい クラスが持つ「変数」を「プロパティ」と呼びます。 クラスが持つ「関数」を「メソッド」と呼びます。 プロパティはpublic、private、protected (アクセス修飾子)で定義します。 クラス内でプロパティやメソッドを参照するときは、$this という変数を使います。 このときプロパティ名に$(ダラー)はつけません。 × $this->$number1   〇 $this->number1 クラス(設計図)からインスタンス(newで定義)を作成。 インスタンス内のプロパティやメソッドにアクセスするには、オブジェクトに対して -> (アロー演算子)を使用します。 $math = new Math; //クラス(Math)からインスタンスを作成。 ※ 作成したインスタンス($math)は、クラスで定義したプロパティ($number1,$number2)とメソッド( add(),minus() )を持ちます。 $result1 = $math->add(); //メソッドにアクセス 静的プロパティ・メソッド staticキーワードを指定することで静的なプロパティ、メソッドが定義できるようになります。(動的もある。新しく作られるものなどは動的。) 静的なプロパティ、メソッドを定義するとインスタンスを生成せずに直接プロパティ、メソッドを呼び出すことができます。 例)※)::(スコープ演算子) class Message {   //メソッドを定義   static function hello() {     return "こんにちは";   }   static function bye() {     return "さようなら";   } } //この時点では設計図(クラスのみ)   //Messageクラスのメソッドを直接呼び出す。(まだインスタンス化していない。) $message1 = Message::hello(); $message2 = Message::bye(); //スコープ演算子のみで呼び出せる。 大切なルーティングは大体route/web.phpに書いてある。 routes/web.phpファイルは、Webインターフェイス用のルートを定義する。 laravel公式ルーティング参照 セッション状態やCSRF保護などの機能を提供するwebミドルウェアグループが割り当てられ、ほとんどのアプリケーションはroute/web.phpでルートを定義する所から始める。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel5.5 ファイルアップロードメソッド(store)を使用せずに画像ファイルを保存

一般的にアップロードされた画像ファイルの保存方法は下記のようになります。Laravelでは、画像アップロードのフォームから送信された画像に関してはstoreメソッドを使用すれば、簡単にサーバーに画像を保存する事が可能なのですが、画像URLのパスを取得して、直接的に画像を保存する場合は若干処理が助長的になってしまうのがネックだと思います(この部分もっとスマートに出来るよ等、アドバイスがあればコメント欄にて教えて頂けますとありがたいです! $path = $request->file('userimage')->store('public/img'); $image = User::find(\Auth::id()); $image->image = basename($path); //imageカラムに保存 $image->save(); 前提 上記のように、アップロードされた画像ファイルの保存処理はLaravelだと簡単に書けるのですが バッチ処理等で画像ファイルのパス(URL)を予めDBに格納した場合の、画像ファイルを保存する方法で少し躓いたのでメモ代わりに記事を執筆します。 今回のケース imagesテーブルに、各画像ファイルのパスが格納されているので、まずは下記のように順番に画像ファイルのパスを取得します。 $all_images = Images::get(['image_name']); foreach ($all_images as $image) {                         //DBに格納されている画像ファイル名を任意に変更したかったので、更新対象となるオブジェクトを取得 $update_image_name = Images::where('image_name', $image->image_name)->first(); //画像ファイルのURLを定義 $url = $image->image_name; //URLからファイル名を取得(今回は画像URLの拡張子を取得して、ファイル名として保存しています) $file_name = substr(strrchr($url,"/"),1);                                                   //画像ファイル名の更新処理 $update_image_name->image_name = $file_name; $update_image_name->save(); } //ここから画像ファイル保存の処理を書く 画像ファイルの保存処理 先程の、//ここから画像ファイル保存の処理を書くの処理を記述していきます。 //①の処理 if (strpos($url, 'AAAAAA') !== false) { $options = array('http' => array('method' => 'GET', 'header' => 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',"ignore_errors"=>true)); $image_downloaded = file_get_contents($url, false, stream_context_create($options)); } else { $image_downloaded = file_get_contents($url); } //②の処理 $tmp = tmpfile(); fwrite($tmp, $image_downloaded); $tmp_path = stream_get_meta_data($tmp)['uri']; Storage::putFileAs('public/img', new File($tmp_path), $file_name); fclose($tmp); ①の処理説明 $image_downloadedという変数に、file_get_contents関数を使用して画像URLから画像データを取得してます。 画像URLにAAAAA(仮のサイト名)という文字列が含まれている場合、画像URLにアクセスしようとするとForriden 403というエラーが出力されて、調べるとUser-Agentの設定が必要との事だったので適当に定義したのですが、エラーが出るので、ignore_errors"=>trueを$optionsという配列に定義して無理やりエラーを出力させないようにしております(この状態で$img_downloadedをデバッグしてみるとAWS関係のエラーが出ましたが、今の私では解決困難だと思ったので、そのまま放置してます) ②の処理説明 ①$tmp = tmpfile(); storage/app/public/imgディレクトリの直下に画像を保存する為に一時ファイルを作成 ②fwrite($tmp, $image_downloaded); 一時ファイルに、画像データを書き込む ③$tmp_path = stream_get_meta_data($tmp)['uri']; 後述する画像保存処理の際、対象の一時ファイルのパスを引数に定義する必要がある為、この部分で一時ファイルのパスを取得する ④Storage::putFileAs('public/img', new File($tmp_path), $file_name) storage/app/public/imgディレクトリ直下に画像ファイルを保存する →Storage::putFileAsを使用して画像ファイルを保存する場合、第一引数に保存する場所、第二引数の保存対象する画像ファイルのパスを指定、第三引数に保存するファイル名を定義する。 ⑤fclose($tmp); 一時ファイルを削除する(削除しないと次の画像ファイルが保存できないので) 以上が、Laravelでアップロード以外の方法で画像保存する場合の処理方法となります! ここまで読んで頂きありがとうございます。 なんでこの部分、こう書いているのか?他にこういう方法あるよ!等アドバイスがあればコメント頂けますと非常に助かります!宜しくお願い致します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

空白投稿もエラーメッセージが表示されない(Laravel)

<エラー概要> ◉LaravelMix学習 Class "App\Http\Controllers\Admin\Profile" not found <仮説> ・ Profileが存在するか確認 ・ 名前空間とファイル名の命名規則相違 ・ ファイルの階層を、Profileの名前を確認 ・ namespace App; → namespace App\Models; ※いずれも変化なし。 <解決方法> ①複数形になっているmodel削除 ②単数系で再度作成 ③validation.phpで日本語表示に設定 modelは原則単数系で命名しないといけないにも関わらず、 複数形で命名していたことが原因。 冷静になって基礎から調べたら解決できました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】フォーム送信時に419 Page Expiredが発生する件について調べてみた

概要 本記事は、PHPフレームワークLaravel入門 第2版で学習している中の疑問・つまづきの備忘録です。 今回はCSRF対策の際のエラー解決と根本原因についてまとめます。 発生したエラー フォーム送信時に以下のエラーが発生しました。 ソースコード index.blade.php <form action="/hello" method="post"> <table> ///////中略 <tr> <th>Message: </th> <td> <input type="text" name="msg" value="{{ old('msg') }}"> </td> </tr> </table> </form> VerifyCsrfToken.php <?php namespace App\Http\Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; class VerifyCsrfToken extends Middleware { protected $except = [ // ]; } 原因 以下の2点が原因です。 index.blade.phpのformタグ内に@csrfディレクティブを記載していない にも関わらず、VerifyCsrfTokenクラスの配列$exceptにCSRF対策から除外するアクション名を記載していない 解決策1(CSRF対策を行いたい場合) index.blade.phpのformタグ内に@csrfディレクティブを追記 index.blade.php <form action="/hello" method="post"> <table> @csrf //これを追記する ///////中略 <tr> <th>Message: </th> <td> <input type="text" name="msg" value="{{ old('msg') }}"> </td> </tr> </table> </form> 解決策2(CSRF対策を行わない・必要ない場合) VerifyCsrfTokenクラスの配列$exceptにCSRF対策から除外するアクション名を追記 ※この場合formタグに@csrfディレクティブの追記は不要 VerifyCsrfToken.php <?php namespace App\Http\Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; class VerifyCsrfToken extends Middleware { protected $except = [ 'hello', //helloアクションではCSRF対策を行わない指定 ]; } なぜ上記の対応を行わないと419 Page Expiredが発生するのか こうすればエラーが解消できる!というのは分かりましたが、なぜこのような対応が必要なのでしょうか。 裏側で何が起こっているのか調べてみました。 答えはLaravel 8.x CSRF保護に記載がありました。 アプリケーションで"POST"、"PUT"、"PATCH"、"DELETE" HTMLフォームを定義するときはいつでも、CSRF保護ミドルウェアがリクエストを検証できるように、フォームに非表示のCSRF_tokenフィールドを含める必要があります。便利なように、@csrf Bladeディレクティブを使用して、非表示のトークン入力フィールドを生成できます。 Laravelを使用しないPHPでの開発の際に必要だったtype="hidden"でvalueにトークンを持つinputタグを自動で生成してくれています。 <form method="POST" action="/profile"> @csrf <!-- Equivalent to... --> <input type="hidden" name="_token" value="{{ csrf_token() }}" /> //生成されるタグのトークン入力フィールド </form> さらに、 webミドルウェアグループへデフォルトで含まれているApp\Http\Middleware\VerificationCsrfTokenミドルウェアは、リクエスト入力のトークンがセッションに保存されたトークンと一致するかを自動的に検証します。この2トークンが一致すれば、認証済みユーザーがリクエストを開始したことがわかります。 つまり今回は index.blade.phpのフォームに@csrfディレクティブが記載されておらず、トークンを持つinputタグが生成されていなかった VerificationCsrfTokenクラスの配列$exceptでCSRF対策を除外したい当該のフォーム送信時のアクション名が追加されていなかった VerificationCsrfTokenで入力トークン検証が失敗した という理由でフォーム送信時に419 Page Expiredが発生したようです。 まとめ CSRF対策する場合は、formタグに@csrfディレクティブを記載する。 しない場合はVerificationCsrfTokenクラスの配列$exceptにCSRF対策から除外するアクション名を追記する。 色々と調べてみると新たな発見があって楽しいですね! 参考文献 PHPフレームワークLaravel入門 第2版 Laravel 8.x CSRF保護
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP基礎

PHPの宣言 HTMLでPHPを宣言するには<?php ?>内に記述する。 また1行の場合は、末尾の;を省略できる。 sample.php <?php echo "Hello World!"; echo "Hello PHP!"; ?> // 1行の時は、セミコロン不要 <?php echo "Hello World!" ?> 変数 変数の宣言と使用 sample.php <?php $name = "Bob" ; echo $name ; echo "私の名前は{$name}です"; ?> 計算 加減乗除 sample.php <?php echo a + b ; echo a - b ; echo a * b ; echo a / b ; echo a % b ; ?> 簡略化した書き方 sample.php <?php $x = $x + 10; → $x += 10; $x = $x - 10; → $x -= 10; $x = $x * 10; → $x *= 10; $x = $x / 10; → $x /= 10; $x = $x % 10; → $x %= 10; ?> インクリメント、デクリメント sample.php <?php // 後置演算 $x += 1; → $x++; $x -= 1; → $x--; // 前置演算 $x += 1; → ++$x; $x -= 1; → --$x; ?> 文字列結合 sample.php <?php $x = "文字"; $y = "連結"; echo $x.$y; // 省略記述 $x .= "連結"; ?> 条件分岐 sample.php <?php if ($x > 10) { // true } elseif (!($x <= 5)) { //否定 // true } else { // false } ?> 比較演算子 条件 記法 より大きい、より小さい <、> 以上、以下 <=、>= 等しい == 等しくない != 論理演算子 条件 記法 かつ && または その他の判定 条件 記法 特定のクラスのインスタンスがどうか インスタンス instanceof クラス名 switch文 sample.php <?php switch($coin){ case 0: echo "表"; break; case 1: echo "裏"; break; default: echo "エラー"; break; } ?> 配列 sample.php <?php // 配列の生成 $names = array("John", "kate", "Bob"); echo $names[0]; // 配列の末尾に追加 $names[] = "Mary"; // 値の上書き $names[1] = "Jane"; ?> 連想配列 sample.php <?php // 配列の生成 $user = array( "name" => "わんこ", "age" => 14, "gender" => "male" ); echo $user["name"]; // 値の追加 $user["level"] = "beginner"; ?> for文 sample.php <?php for($i = 1; $i <= 100; $i++){ if($i % 3 == 0){ continue; }elseif($i >90){ break; } echo $i ."<br>"; } ?> foreach文 sample.php <?php $towns = array("東京", "大阪", "京都"); foreach($towns as $town){ each $town." "; } $user = array( "name" => "わんこ", "age" => 14, "gender" => "male" ); foreach($user as $key => $value){ echo $key." ".$value." "; } ?> while文 sample.php <?php $i = 1; while($i <=100){ echo $i; $i++; } ?> HTMLに埋め込む場合の書き方(推奨) HTML内でfor文やif文など記述するときは、可読性を上げるため単一行で<?php ?>を完結させるようにした方が良い。 先頭の{→:にする 末尾の}→end〇〇にする(endfor,endif,endswitchなど) sample.php <?php $towns = array("東京", "大阪", "京都") ?> <?php foreach($towns as $town): ?> // { → : <p><?php echo $town." " ?></p> <?php endforeach ?> // } → end〇〇 関数 sample.php <?php // 関数生成 function printCircleArea($radius){ echo $radius * $radius; } function getSum($num1, $num2){ return $num1 + $num2; } // 関数呼び出し printCircleArea(10); $sum = getSum(1,3); ?> クラス sample.php <?php // クラス class Menu{ public $name; //コンストラクタ public function __construct(){ $this->name; } public function hello(){ echo '名前:'.$this->name; } } // インスタンス生成 $curry = new Menu(); // プロパティ値の設定、呼び出し $curry->name = 'CURRY'; echo $curry->name; // メソッド呼び出し $curry->hello(); ?> プロパティ=インスタンスが持つデータのこと メソッド =インスタンスに関連する処理(関数)のこと 他ファイルの読み込み(インポート) menu.php <?php class Menu{ public $name; public function __construct($name){ $this->name = $name; } public function hello(){ echo '名前:'.$this->name; } } ?> data.php <?php require_once("menu.php"); $curry = new Menu("CURRY"); ?> index.php <?php require_once("data.php") ?> <body> <p> <?php echo $curry->name ?> </p> </body> カプセル化 プロパティをpublic→privateにし、gettersetterで読み書き込みするようにする。 menu.php <?php class Menu{ private $name; //public → private : // getter public function getName(){ return this->name; } // setter public function setName($name){ $this->name = $name; } } ?> data.php <?php require_once("menu.php"); $curry = new Menu(); $curry->setName("CURRY"); //setterでプロパティ設定 ?> index.php <?php require_once("data.php") ?> <body> <p> <?php echo $curry->getName() ?> </p> // getterでプロパティ取得 </body> クラスプロパティ 個々のインスタンスがもつデータ(プロパティ)ではなく、クラスがもつデータをクラスプロパティという。 クラスプロパティはstaticを用いて定義する。 クラスプロパティにアクセスする場合はクラス名::$クラスプロパティ名と定義する(publicの場合) クラス内でクラスプロパティにアクセスする場合はself::$クラスプロパティ名と定義する sample.php <?php class Menu{ // クラスプロパティ private static $count = 4; //static付与 public function __construct(){ self::$count++; //クラス内でのアクセス } public static function getCount(){ //static付与 return self::$count; } } echo "メニュー:".Menu::$count."品"; // クラスプロパティの呼び出し(publicの場合) echo "メニュー:".Menu::getCount()."品"; //クラスプロパティの呼び出し ?> 継承 継承を用いて新しく子クラスを定義するときは、「class 子クラス名 extends 親クラス名」のように書く sample.php <?php class Drink extends Menu{ : } $coffee = new Drink("COFFEE", 600, .....); echo $coffee->getName(); ?> オーバーライド 親クラスに対するメソッドの上書きを「オーバーライド」と言う。 オーバーライドの際に親クラスで定義したメソッドを呼び出したいときには、「parent::メソッド名」とすることで親クラスのメソッドを実行することができる。 drink.php <?php class Drink extends Menu{ private $type; public function __construct($name, $price, $image, $type){ parent::__construct($name, $price, $image,); // 親クラスの呼び出し this-> type = $type; } } ?> menu.php <?php class Menu{ protected $name; protected $price; protected $image; public function __construct($name, $price, $image){ this-> name = $name; this-> price = $price; this-> image = $image; } } ?>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel8.x】Laravel + Ngram + Observerを利用した全文検索機能の実装ハンズオン

はじめに 本記事は以下の続編になります。 https://qiita.com/naoki-haba/items/ace7a5d1e0d9d72ed040 続編記事を書くことにした経緯 シンプルなテーブル構成の場合は以下の通りにすれば全文検索の準備は整います DB::statement("ALTER TABLE shops ADD free_word TEXT as (concat(IFNULL(age, ''), ' ',IFNULL(name, ''), ' ',(case gender_id when 1 then '男性' when 2 then '女性' else '' end), ' ')) STORED"); しかし、複雑な要因(複数テーブルとの外部結合が必要な場合etc)の場合に、上記の記述をすることに苦労したので、今回は対処方法の選択肢の1つとしてご紹介させていただきます。 記事の流れ 1.既存のDDLからfree_wordカラムを削除する 2.ダミーデータを投入する 3.free_wordカラムを追加する 4.登録・更新イベントをディスパッチする処理を追加する 5.Artisanコマンドを作成する 6.作成したArtisanコマンドを実行。  free_wordカラムにデータを投入する 事前準備 docker-compose up -d docker-compose exec app bash composer install composer update cp .env.example .env php artisan key:generate php artisan storage:link chmod -R 777 storage bootstrap/cache Laravel + Ngram + Observerを利用した全文検索機能の実装ハンズオン 1.既存のDDLからfree_wordカラムを削除する 変更用のmigrationファイルを生成します php artisan make:migration change_free_word_to_shops --table=shops migrationを定義 backend/database/migrations/2022_01_06_185439_change_free_word_to_shops.php class ChangeFreeWordToShops extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('shops', function (Blueprint $table) { $table->dropColumn('free_word'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('shops', function (Blueprint $table) { DB::statement("ALTER TABLE shops ADD free_word TEXT as (concat(IFNULL(age, ''), ' ',IFNULL(name, ''), ' ',(case gender_id when 1 then '男性' when 2 then '女性' else '' end), ' ')) STORED"); }); } migrationを実行する php artisan migrate DDLを確認し,shopsテーブルにfree_wordカラムがなければOKです ngram-docker-laravel docker-compose exec db bash mysql -u root -p Enter password: password mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | laravel_local | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.01 sec) mysql> use laravel_local; mysql> show tables; +-------------------------+ | Tables_in_laravel_local | +-------------------------+ | failed_jobs | | migrations | | password_resets | | personal_access_tokens | | shops | | users | +-------------------------+ 6 rows in set (0.00 sec) DESC shops; +------------+-----------------+------+-----+-------------------+-----------------------------------------------+ | Field | Type | Null | Key | Default | Extra | +------------+-----------------+------+-----+-------------------+-----------------------------------------------+ | id | bigint unsigned | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | | NULL | | | age | int unsigned | NO | | NULL | | | gender_id | smallint | NO | | NULL | | | created_at | timestamp | NO | | CURRENT_TIMESTAMP | DEFAULT_GENERATED | | updated_at | timestamp | NO | | CURRENT_TIMESTAMP | DEFAULT_GENERATED on update CURRENT_TIMESTAMP | +------------+-----------------+------+-----+-------------------+-----------------------------------------------+ 6 rows in set (0.01 sec) 2.ダミーデータを投入する 実行するSeedファイル backend/database/seeders/DummyShopsSeeder.php <?php namespace Database\Seeders; use App\Models\Shop; use Illuminate\Database\Seeder; class DummyShopsSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $data = [ [ 'name' => 'サンプル太郎', 'age' => 25, 'gender_id' => 1 ], [ 'name' => 'サンプル花子', 'age' => 30, 'gender_id' => 2 ], [ 'name' => 'サンプル二郎', 'age' => 20, 'gender_id' => 1 ], ]; (new Shop())->query()->insert($data); } } Seedファイルを実行 php artisan db:seed --class=DummyShopsSeeder Seed結果を確認し登録できていれば成功です mysql> select * from shops; +----+--------------------+-----+-----------+---------------------+---------------------+ | id | name | age | gender_id | created_at | updated_at | +----+--------------------+-----+-----------+---------------------+---------------------+ | 1 | サンプル太郎 | 25 | 1 | 2022-01-07 04:19:43 | 2022-01-07 04:19:43 | | 2 | サンプル花子 | 30 | 2 | 2022-01-07 04:19:43 | 2022-01-07 04:19:43 | | 3 | サンプル二郎 | 20 | 1 | 2022-01-07 04:19:43 | 2022-01-07 04:19:43 | +----+--------------------+-----+-----------+---------------------+---------------------+ 3 rows in set (0.01 sec) 3.free_wordカラムを追加する 再度free_wordカラムを追加するmigrationを作成します php artisan make:migration add_free_word_column_to_shops --table=shops migrationファイルを定義します backend/database/migrations/2022_01_07_162519_add_free_word_column_to_shops.php class AddFreeWordColumnToShops extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('shops', function (Blueprint $table) { DB::statement("ALTER TABLE shops ADD free_word TEXT"); DB::statement("ALTER TABLE shops ADD FULLTEXT index ftx_free_word (free_word) with parser ngram"); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('shops', function (Blueprint $table) { $table->dropColumn('free_word'); }); } } migrationを実行する php artisan migrate DDLを確認しshopsテーブルにfree_wordカラムが追加されていればOKです ngram-docker-laravel docker-compose exec db bash mysql -u root -p Enter password: password mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | laravel_local | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.01 sec) mysql> use laravel_local; mysql> show tables; +-------------------------+ | Tables_in_laravel_local | +-------------------------+ | failed_jobs | | migrations | | password_resets | | personal_access_tokens | | shops | | users | +-------------------------+ 6 rows in set (0.00 sec) mysql> select * from shops; +----+--------------------+-----+-----------+---------------------+---------------------+-----------+ | id | name | age | gender_id | created_at | updated_at | free_word | +----+--------------------+-----+-----------+---------------------+---------------------+-----------+ | 1 | サンプル太郎 | 25 | 1 | 2022-01-08 01:54:17 | 2022-01-08 01:54:17 | NULL | | 2 | サンプル花子 | 30 | 2 | 2022-01-08 01:54:17 | 2022-01-08 01:54:17 | NULL | | 3 | サンプル二郎 | 20 | 1 | 2022-01-08 01:54:17 | 2022-01-08 01:54:17 | NULL | +----+--------------------+-----+-----------+---------------------+---------------------+-----------+ 4.登録・更新イベントをディスパッチする処理を追加する さて,ここまでで全文検索用のカラムの作成が完了しました。 ですが見ての通りfree_wordカラムはNULLなのでこれでは全文検索ができません。 そこで、登録・更新イベントをディスパッチして自動的にfree_wordカラムに追加する値を生成していきます オブザーバーを作成します php artisan make:observer ShopObserver --model=Shop オブサーバーを定義 <?php namespace App\Observers; use App\Models\Shop; class ShopObserver { /** * save()イベントを検知する * @param Shop $shop * @return void */ public function saved(Shop $shop) { $collect = collect($shop); $id = $collect->get('id'); $name = $collect->get('name'); $age = $collect->get('age'); $genderId = $collect->get('gender_id'); if (!is_null($genderId)) { $gender = (int)$genderId === 1 ? '男性' : '女性'; } else { $gender = null; } $freeWord = $age . ' ' . $id . ' ' . $name . ' ' . $gender; $data = [ 'id' => $id, 'name' => $name, 'age' => $age, 'gender_id' => $genderId, 'free_word' => $freeWord, ]; (Shop::query()->where('id', $id))->update($data); } } オブサーバーを登録 backend/app/Providers/EventServiceProvider.php class EventServiceProvider extends ServiceProvider { /** * The event listener mappings for the application. * * @var array<class-string, array<int, class-string>> */ protected $listen = [ Registered::class => [ SendEmailVerificationNotification::class, ], ]; /** * Register any events for your application. * * @return void */ public function boot() { Shop::observe(ShopObserver::class); } } 5.Artisanコマンドを作成する コマンド生成 php artisan make:command UpdateFreeWordByShop コマンド定義 class UpdateFreeWordByShop extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'update:free-word-by-shop'; /** * The console command description. * * @var string */ protected $description = 'shopsテーブルのfree_wordを登録するコマンド'; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * Execute the console command. * * @return void */ public function handle() { $updateTarget = Shop::query()->pluck('id'); foreach ($updateTarget as $id) { $target = Shop::find($id); $result = $target->save(); if (!$result) { echo "店ID:{$id}の登録中にエラーが発生しました。終了します\n"; exit(); } echo "{$id}完了\n"; } echo "処理完了。終了します。\n"; exit(); } } 6.作成したArtisanコマンドを実行してfree_wordカラムにデータを投入する 作成したコマンドが登録されていることを確認 php artisan update update:free-word-by-shop shopsテーブルのfree_wordを登録するコマンド コマンドを実行する php artisan update:free-word-by-shop 1完了 2完了 3完了 処理完了。終了します。 free_wordにデータが登録されているかを確認 free_wordに値が登録されていれば成功です! mysql> select * from shops; +----+--------------------+-----+-----------+---------------------+---------------------+--------------------------------+ | id | name | age | gender_id | created_at | updated_at | free_word | +----+--------------------+-----+-----------+---------------------+---------------------+--------------------------------+ | 1 | サンプル太郎 | 25 | 1 | 2022-01-08 01:54:17 | 2022-01-07 19:25:39 | 25 1 サンプル太郎 男性 | | 2 | サンプル花子 | 30 | 2 | 2022-01-08 01:54:17 | 2022-01-07 19:25:39 | 30 2 サンプル花子 女性 | | 3 | サンプル二郎 | 20 | 1 | 2022-01-08 01:54:17 | 2022-01-07 19:25:39 | 20 3 サンプル二郎 男性 | +----+--------------------+-----+-----------+---------------------+---------------------+--------------------------------+ 3 rows in set (0.00 sec) おわりに 読んでいただきありがとうございます。 今回の記事はいかがでしたか? ・こういう記事が読みたい ・こういうところが良かった ・こうした方が良いのではないか などなど、率直なご意見を募集しております。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel備忘録

はじめに Laravelのプロジェクトを作成するにあたって毎度実行することは変わらないが、覚えるのが大変なものを備忘録としてまとめ上げた。 目次 プロジェクト初期設定 migrationファイル作成 Herokuデプロイ プロジェクト初期設定 Laravelプロジェクトを作成 composer create-project laravel/laravel --prefer-dist プロジェクト名 サーバーを起動 cd プロジェクト名 php artisan serve Bootstrapのダウンロード & Login関連の見た目を作る composer require laravel/ui npm install npm run dev npm run dev php artisan ui bootstrap --auth モデルの作成 php artisan make:model モデル名(1文字目大文字&単数形) -m php artisan migrate 再度内容を修正したい場合 php artisan migrate:fresh migrationファイル作成 構造 up関数 migrateしたときに実行されたい部分を記述 down関数 rollbackしたときに実行されたいコードを記述 データ型 自動増分 increments(‘id’) … 符号なしINTを使用した自動増分ID(主キー) bigIncrements(‘id’) … 符号なしINTを使った自動増分ID(主キー)。 unsignedBigInteger(‘id’) … 符号なしBIGINTを使用した自動増分ID(主キー) 数字 integer(‘カラム名’) … 数値データカラム bigInteger(‘カラム名’)… 数値ビッグデータカラム double(‘カラム名’, 桁数, 小数点以下桁数) … 小数カラム 日付 date(‘カラム名’) … 日付カラム(日付のみ) time(‘カラム名’) … 時間カラム(時間のみ) dateTime(カラム名) … 日時カラム timestamp(‘カラム名’) … TIMESTAMPカラム timestamps() … created_atとupdate_atカラム nullableTimestamps() … NULL値を許すtimestamps() softDeletes() … 論理削除のためのNULL値可能な deleted_at カラム追加 文字 char(‘カラム名’, 長さ) … 長さを指定する文字列カラム string(‘カラム名’, ['長さ']) … VARCHARカラム。第2引数で長さ指定も可、デフォルト255文字。 text(‘カラム名’) … TEXTカラム longtext('カラム名') … LONGTEXTカラム その他 binary(‘カラム名’) … バイナリデータカラム boolean(‘カラム名’) … 真偽値カラム enum(‘カラム名’, [‘定数’, ‘定数’]) … ENUMカラム json(‘カラム名’) … JSONフィールドカラム Herokuデプロイ Heroku App作成 ログインし、右上 NewからCreate New Appを選択 App Nameを入力 → Create Appを選択 settings→BuildpacksにNode.jsとPHPを追加 DB登録 Resources→Add-onsにJawsDB MySQLと入力 Submit Order Formを押す ターミナルでプロジェクト内まで移動 heroku git:remote -a [Herokuアプリ名]を実行 set git remote heroku to https://git.heroku.com/[Herokuアプリ名].gitを実行 git push heroku masterを実行(この時点で一度デプロイが完了する) heroku configを実行し、JAWSDB_URLを取得 書式:mysql://[DB_USERNAME]:[DB_PASSWORD]@[DB_HOST]:[DB_PORT]/[DB_DATABASE] settings→Config VarsのReveal Config Varsを押し、上の情報をそれぞれ入力 同じ場所にAPP_KEY(.env.exampleに表記あり)とAPP_URL(Heroku画面右上のOpen Appで開くデフォルトURL)も追加 メールを使用している場合はその情報も入れる ファイル準備 .env.exampleファイルを複製し、アプリファイル直下に.env.herokuファイルを作成 & 内容をDB登録の内容と合わせる composer.jsonファイルの"scripts"に以下を追加 "compile": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ] アプリファイル直下にProcfileを作成し、中身にweb: vendor/bin/heroku-php-apache2 public/を追加 アプリファイル直下に.htaccessファイルを作成、中身に以下を入れる <IfModule mod_rewrite.c> <IfModule mod_negotiation.c> Options -MultiViews </IfModule> RewriteEngine On RewriteCond %{REQUEST_FILENAME} -d [OR] RewriteCond %{REQUEST_FILENAME} -f RewriteRule ^ ^$1 [N] RewriteCond %{REQUEST_URI} (\.\w+$) [NC] RewriteRule ^(.*)$ public/$1 RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ server.php </IfModule> app/Providers/AppServiceProvider.phpのboot関数に以下の記述を追加 Paginator::useBootstrap(); Schema::defaultStringLength(191); if (request()->isSecure()) { \URL::forceScheme('https'); } app/Providers/RouteServiceProvider.phpのpublic const HOMEを/home→/に変更 app\Http\Middleware\TrustedProxiesのprotected $proxies直後に= "*"追加 GitHub Auto Deploy設定 Heroku画面のDeploy→Deployment methodでGitHubを選択 GitHubのリポジトリーを選択 Enable Automatic Deploysを選択 migration heroku run php artisan migrate デプロイ方法 ファイルを編集 GitHubにpushする もしくは Heroku画面からDeploy→GitHub→Deploy Branchを選択
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPで日付時刻を取得するならCarbon型が便利

背景 テストコードを書く時にさっと日付を戻したり進めたりする時にすごい便利だったので使い方をメモします。 Carbon型とは PHPのDateTimeクラスを継承して拡張された日時操作ライブラリのことです。 導入方法 Composerでインストールすればすぐに使えます。 composer require nesbot/carbon 使い方 <?php use Carbon\Carbon; echo Carbon::now(); // 現在の日時を取得(2022-01-01 00:00:00) echo Carbon::today()->toDateString(); // 日付のみ取得(2022-01-01) $carbon = Carbon::create(2022, 1, 1, 10, 20, 30); // 引数で時刻を設定 echo $carbon->year; // 2022 echo $carbon->format('Y年m月d日'); // フォーマットを指定できます(2022年01月01日) CarbonImutable型 Carbon型の他にCarbonImutable型というのがあります。 CarbonImutable型は一度オブジェクトが作成された後にメソッドを呼んでも内部状態が変わらない性質があります。 具体的な違いを以下の例で説明します。 // 現在時刻を操作 \Carbon\Carbon::setTestNow('2020-01-01T00:00:00'); // 現在時刻を取得 $now = \Carbon\Carbon::now(); // 翌日の時刻を取得 $tomorrow = $now->addDay(); 上記のように実装した場合、それぞれの出力結果は以下のようになります。 now: '2020-01-02T00:00:00' tomorrow: '2020-01-02T00:00:00' $nowの値も変わってしまいます。 これは$nowと$tomorrowが同一のオブジェクトであることが原因です。 $nowの中身を変えないようにするにはcopy()を使用します。 // 現在時刻を取得 $now = \Carbon\Carbon::now(); // 翌日の時刻を取得 $tomorrow = $now->copy()->addDay(); // 出力結果 now: '2020-01-01T00:00:00' tomorrow: '2020-01-02T00:00:00' CarbonImutable型を使えばcopy()を使わなくても大丈夫です。 \Carbon\CarbonImmutable::setTestNow('2022-01-01T00:00:00'); $now = \Carbon\CarbonImmutable::now(); $tomorrow = $nowImm->addDay(); // 出力結果 now: '2020-01-01T00:00:00' tomorrow: '2020-01-02T00:00:00' 状況に応じて使い分ける必要はありますが、時刻操作をする際はかなり便利なので覚えておくと良さそうです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む