20210608のPHPに関する記事は15件です。

Laravel詳細画面作成

詳細画面作成作った。 //コントローラー public function detail($id){      //データベースから特定のIDを探してくる $posts = Post::find($id); return view('posts.details', compact('posts'));      //変数postsをdetails.blade.phpに送る } //details.blade.php <a href="{{ route('posts.detail', ['id'=>$posts->id]) }}">応募する</a> コントローラーから受け取った$posts変数の中にあるidをさらにweb.phpに送るため新しくid変数を作って代入。 //web.php Route::get('/posts/details/{id}', 'PostController@detail')->name('posts.detail'); idを受けっとて{id}の中に入る。 流れは理解した。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

N + 1 問題 Eagerloadによる解決

はじめに SQLを触っていると必ず目に入るN+1問題。 なんとなくは分かっているが整理のためにもN+1問題について自分なりのメモを残しておく。 N+1問題 N+1問題は端的に言うと、SQLがたくさん発行されてしまい動作が遅くなる問題のことですね。 もう少し詳しくいうと ・N件のデータ行を持つテーブルを読みだすのに1回 ・別のテーブルから、先述のテーブルの各行に紐づくデータを(1件ずつ)読み出すのに計N回 なのでNが増えれば増えるほど処理が重くなってしまうんですね。 例を挙げますと、下記のようなBookモデルがあって、各Bookと1対1のAuthorモデルと繋げるリレーションメソッド authorが定義されていたとします。 Book.モデル <?php namespace App; use Illuminate\Database\Eloquent\Model; class Book extends Model { /** * この本を書いた著者を取得 */ public function author() { return $this->belongsTo('App\Author'); } } 次のように、全BookのAuthorの名前を取得する場合、次のように書くと N+1 問題にぶつかります。 $books = App\Book::all(); foreach ($books as $book) { echo $book->author->name; } このコードのSQLの中身は SELECT * FROM books; SELECT * FROM authors WHERE book_id = 1; SELECT * FROM authors WHERE book_id = 2; SELECT * FROM authors WHERE book_id = 3; . . Book が全部で100 レコードあるとしたら、100+1で合計101ものクエリが発行されてしまう。 このうち大部分の SQL は book_id が違うだけなので無駄が多い。 処理も重くなってしまいますね。 これをLaravelではEagerLoad で削減できます。 全Bookを取得し、authorをEagerLoadしておく $books = App\Book::with('author')->get(); foreach ($books as $book) { echo $book->author->name; } $books = App\Book::with('author')->get()のwithの部分がEagerLoadです。 あらかじめauthorを行ったものを入れているわけですね! 処理はこのようにスッキリとなりました。 with(author)を.用いた場合 SELECT * FROM books; SELECT * FROM authors WHERE id IN (1, 2, 3, 4, 5, ...) いろいろなEagerLoad さっき使ったwith。複数のリレーションに使用できる。 複数のリレーションに.対するEagerLoad $books = App\Book::with(['author', 'publisher'])->get(); ↓リレーションで取得した先のモデルからさらに別のリレーションに繋げる場合に使う ネストした.EagerLoad $books = App\Book::with('author.contacts')->get(); ↓親のモデルを取得した後に、ある条件によって eagerload するかを決めたいときなど。 with の代わりに load を使う。 遅延.EagerLoad $books = App\Book::all(); if ($someCondition) { $books->load('author', 'publisher'); } ↓使用するリレーションにさらに制約をかける場合。 例では、postのtitleにfirst という言葉を含むという制約をかけた。 where だけでなく orderBy など他のクエリビルダも使える。 User.モデル <?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * ユーザーの全ポストの取得 */ public function posts() { return $this->hasMany('App\Post'); } /** * タイトルにfirstの文字が入るPostを取得する */ public function postsInTitleFirst() { return $this->posts()->where('title', 'like', '%first%'); } } EagerLoadへの.制約 $user = App\User::with('postsInTitleFirst')->find(1); $posts = $user->postsInTitleFirst; foreach ($posts as $post) { echo $post->title; } まとめ 投稿一覧とかなんらかの一覧を取得する時にはこのN+1問題が必ず発生すると思います。 LaravelではEagerLoadによってこれを防ぐことができるので、 特にリレーションメソッドを使う際には注意をする。 ま、要するにリレーションメソッドを使用したものを再度入れ込んでいきましょうってことですね!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravel 時間を指定してバッチを実行する 個人メモ

目的 laravelにて時間を指定してバッチを実行する方法をメモ的にまとめる 方法 下記ファイルを開く。 アプリ名ディレクトリ/app/Console/Kernel.php Kernelクラスのschedule()メソッドに時間で実行したいバッチの情報を記載する。 アプリ名ディレクトリ/app/Console/Kernel.php /** * Define the application's command schedule. * * @param \Illuminate\Console\Scheduling\Schedule $schedule * @return void */ protected function schedule(Schedule $schedule) { $schedule->command('command:バッチ名')->実行タイミングの指定; } 例えば毎日12:00に実行指定ほしいバッチを登録する場合下記のように記載する。 アプリ名ディレクトリ/app/Console/Kernel.php /** * Define the application's command schedule. * * @param \Illuminate\Console\Scheduling\Schedule $schedule * @return void */ protected function schedule(Schedule $schedule) { $schedule->command('command:バッチ名')->dailyAt('12:00'); } 参考文献 Laravel 6.x タスクスケジュール
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ゲームのマスターデータのフォーマットはcsvよりtsvがオススメ

概要 モバイルゲームを開発する際は、ExcelやGoogleスプレッドシートなどでマスターデータを管理し、csvとしてエクスポートしてgit管理するといった運用をわりと見かけます。 最近、クライアント/サーバー間でやりとりするマスターデータのフォーマットをcsvからtsvに変えたところ、とても使い勝手が良かったため紹介します。 tsvとは タブを区切り文字として使用したデータ形式。 csvはカンマを区切り文字とするが、そのカンマ(,)がタブ(\t)になったもの。 具体的には以下のようなテキストデータとなる。 csv name,hp,atk スライム,10,2 ゴブリン20,4 tsv name\thp\tatk スライム\t10\t2 ゴブリン\t20\t4 tsvのメリット 言語やライブラリへの依存がとても少なく、シンプルにパースすることができる わりと最近のシステムだとcsvファイルとして普通に読み込める コードサンプル C#でtsvを出力 var line = string.Join("\t", fields); C#でtsvをパース var fields = line.Split('\t'); PHPでtsvを出力 $line = implode('\t', $fields); PHPでtsvをパース $fields = explode('\t', $line); tsvのデメリット 文字列中にタブを使用したい時は別の文字列に置換する必要がある 古いシステムだと読み込めないものがある csvのメリット とても一般的に使用されている 以下略 csvのデメリット tsvのようにシンプルにパースすることができない 文字列としてカンマを使用したい時はダブルクォーテーションで囲う必要がある ダブルクォーテーションを使用している箇所と使用していない箇所が混在しがち tsvに比べてロジックが複雑なためパースコストが高い ライブラリによっては細部の対応オプションが異なる まとめ tsvからcsvへの変換は容易なため、非エンジニアがマスターデータを参照する時はcsvとして扱いつつ、クライアント/サーバー間のやりとりはtsvとして扱うなど、柔軟に使い分けられると双方のメリットが最大限に活かせるのではないかと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

さくらvps独自ドメイン設定備忘録

自分でサービスを公開する際URLを「サービス名.com」にしたかったので以下のサイトたちを参考にして独自ドメイン取得、セッティングしました。自分の行った設定通りに記事を残したかったのですが、ログが残っていないので参考になるURLの記載。 完全に自分用の備忘録です。 自分はドメインもサーバーもさくらインターネットなので簡単でした。他の会社でドメインを取得した場合の手順は分かりません、、 ~さくらインターネットで独自ドメイン取得サイト~ https://domain.sakura.ad.jp/?utm_source=vps&utm_medium=cp ~さくらインターネットで取得した独自ドメインを自身のサーバーにセットする方法参考~ https://webkaru.net/vps/sakura-domain-setting/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

さくらvps独自ドメイン設定https構築の備忘録

自分でサービスを公開する際URLを「https://サービス名.com」にしたかったので以下のサイトたちを参考にして独自ドメイン取得、セッティングしました。自分の行った設定通りに記事を残したかったのですが、ログが残っていないので参考になるURLの記載。 https通信の構築もしたかったのですがなかなかにハードでした、、 その時にかなり助けとなったサイトや記事を貼ります。 完全に自分用の備忘録です。 レンタルサーバーからwebサービスの公開をした者からするとvpsでの環境設定は結構大変だと感じましたし、そう感じてる方もいらっしゃると思うので是非参考にしてみてはどうでしょう。https通信に関してはレンタルサーバーだと自身でSSL証明書を準備する必要がないので安易でした。 自分はドメインもサーバーもさくらインターネットなので簡単でした。他の会社でドメインを取得した場合の手順は分かりません、、 自身の利用ツール サーバー:さくらのVPS ドメイン:さくらインターネット SSL証明書:さくらのSSL 参考記事一覧 ~さくらインターネットで独自ドメイン取得サイト~ https://domain.sakura.ad.jp/?utm_source=vps&utm_medium=cp ~さくらインターネットで取得した独自ドメインを自身のサーバーにセットする方法参考~ https://webkaru.net/vps/sakura-domain-setting/ ~自分のサービスをhttps通信にするために~ https://qiita.com/yoshizaki_91/items/e6f39a5bfb99900b44b2 https://webdirector-blog.com/c_3/p_35/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

さくらvpsララベルデプロイした大まかな流れ一覧(備忘録)~vps契約~#1

約半年前ほどからwebアプリを作成しており、レンタルサーバーにまでデプロイ完了、 実際に公開しております(機能自体はまだまだしょぼい、、)。 ただ、レンタルサーバーだけでは入れられないライブラリやもジュールがかなりあり、 不便だと感じたため今後新しい機能が追加し、今後ユーザーが増える事を視野に入れてvpsに移行する事を決めました。 ユーザーが利用した際DBのバックアップ、移項に自信がなかったためvpsでリスタートすることにしました。 やった事の大まかな流れ 1vps申し込み(今回はこれを説明する。) 詳細は以下になります。 とりあえずユーザー数が増えたらスペックを増やそうと思ったのでとりあえずは1GのCOREを申し込み。 石狩り、大阪、東京の東京(990円)を選択しました。どこがでチラッとみた情報なので間違いかもしれませんが、サーバが配置されている都市だと聞きました。自分はシティボーイなので距離が近い東京にしましたww 応募方法などはサイトに従っていただければ可能なのでここでは端折ります。 なんやかんやで申し込みが完了して画面にログインします。idやパスワードは登録したメールに記載されますのでそちらを入力します。後述する方法で変更する方が良いかもしれません!!セキュリティ的にも良くないと思ったので自分は変えました。 ログイン後以下の画面に遷移します。少し見辛いですが、名称未設定というところが今回契約したサーバーとなります。追加していくとこの概要が増えます、 次回は便利なスタートスクリプトを使ってCentOSをインストールしていきたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQL PDOに接続するときのメモ

はじめに はじめは勉強のためにMDB2を先にやろうかと思ったら、 いろいろと情報がないこともあり、時間もなく断念。 PDOならなんとかなるかと思うのと、PDOで試験出るならやっぱりやらないとあかんかなと 思ったので、つないでみることにした コマンドでMYSQLに入る https://qiita.com/fuwamaki/items/194c2a82bd6865f26045 これを参考にしました。 なぜかというと、まずはMDB2から入ろうかと思ってここからやっていたのがきっかけ。 で、すでに入ったことがあるので、 コマンドラインから mysql -u root -h localhost -p ここでログイン SHOW DATABASE ここでデータベースを確認して、すでに作った「test」があるのを確認 SELECT User,Host FROM mysql.user; 次にユーザーについても確認 rootがあったので、パスワードを設定して退出 create user 'root' identified by '******'; quit; ※*はもちろんパスワードを入れる create user 'pdouser' identified by '******'; SELECT user, host FROM mysql.user; flush privileges; あたらしくユーザーとパスを作って ユーザー一覧にあるのを確認して 反映。 なぜかhostが%になってた。 GRANT ALL PRIVILEGES ON test.* to 'atsublue'@'localhost'; ここでなぜかERROR 1410 (42000)がでて、できない。 ちなみにこれはシンタックスエラーがあるんじゃないかと言われている。 MAMPのPHPは5.7.32で、ERROR 1410 (42000)が出るはずはない。 MYSQL8ならば、下記の様な方法ができるが。 https://qiita.com/yamateion/items/5509484aaf1a02e9cc57 そこではたと気づいた。 MAMPからmyPHPAdominに行ってDBがあるのかどうか調べようと思った。 入り方は下記 https://qiita.com/mako0104/items/cb677d04922a3d9672ae すると、 DBにtestはおろか、ユーザーにpdouserが入っていない。 ここでつまった。 でやっと気づいた。 冒頭で、なぜか、MAMP内にはいらず、MySQLに入っていたことに。 それだと、前にHomeblewで入れていたMYSQLに入ってしまうことに。 あわてて、mysqlのバージョンを確認した。 やはり、8.0だった笑 https://qiita.com/rokumura7/items/b270acb9550efddd5fe5 ということで、 cd /Applications/MAMP/Library/bin/ ./mysql -u root -p ここで、パスワード入力画面が出るのだが、 私はずっと勘違いしていたせいで、MAMPのMySQLには入っていないので パスワードはrootのはずだ、と思ってこれを入れるとログインできた さてデータベースを作り、show databasesで作ったDBがあるのを確認。 下のpdotestdbはDB名で、好きなものを入れてOK create database pdotestdb; 次に、ユーザーも作り、このユーザーでDBにアクセスできるように設定 pdouserがユーザー名で、 localhostはホスト名 pdotestdbは上記で設定したDB名 mysql> create user 'pdouser'@'localhost' identified by '(好きなパスワード)'; mysql> grant all privileges on pdotestdb.* to 'pdouser'@'localhost'; ここまでうまくいった。 さて、ここでphpからPDOでmySQLにつなぐ 下のポート番号はMAMPにあったポート番号。 charsetはこれしないと文字化けになるのであったほうがいい。 index.php <?php $dsn = 'mysql:host=localhost;port:8889;dbname=pdotestdb;charset=utf8'; $DB_user = 'pdouser'; $DB_pass = '(上記で設定したパスワード)'; try{ $db = new PDO($dsn,$DB_user,$DB_pass); echo "接続成功"; } catch(PDOException $e){ echo "接続失敗" . $e->getMessage() ."<br>"; ?> 接続成功 と書かれた画面が出力され、うまくいきました! 追記 パスワードがrootのままだとセキュリティ上危ないので変更 下記のリンクを参考に ただ、quitしたあとにもとに戻るのに、また、 cd /Applications/MAMP/Library/bin/ しないといけないので面倒だ。。。 そんなときには「パスを通す」ことをやればいいらしいです。(メモ) https://webkaru.net/mysql/mamp-command-line/ 今回はテストと勉強でつないでいるだけなので、今はいいかなと思ってやっていない。 必要であれば上記を参考に。 さらに追記 上のようにやっても、接続はできても、それ以降のことができないことがあった。 そのときは、今使いたいDB を、useすれば使えるようになる。 $db=>exec('use テーブル名'); このあとに、SQL 文をつくって実行する感じ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker-composeのcomposeファイルの書き方【LAMP環境構築ベース】

Docker-composeを使って、PHPとMySQLを動かすために、LAMP環境を作成したので、そのcomposeファイルを元にdocker-composeの概要をまとめてみました。 Docker-composeとは Docker-composeとは、複数のコンテナをつなげて管理することのできるツールです。 Docker-composeを使うことで、簡単にコンテナ間の通信を行うこともできます。 Dockerイメージの作成方法やコンテナ起動時の設定などを「docker-compose.yml」に記述することで、「docker compose」コマンドでまとめて操作できるようになります。 Docker-composeは設定を全てyml形式で記述します。 そのファイルをcomposeファイルといい、ここでdocker fileでイメージをビルドして、コンテナを立ち上げる事ができます。 また、環境関数を使うことも可能です。 今回はこのdokcer-composeを使って、 mysqlとphp-apache、phpmyadminのコンテナを作り、LAMP環境を作成します。 composeファイルについて docker-compose.yml version: "3.7" services: mysql: image: mysql:5.7 volumes: - db_data:/var/lib/mysql restart: always environment: MYSQL_ROOT_PASSWORD: "password" phpmyadmin: depends_on: - mysql image: phpmyadmin/phpmyadmin environment: PMA_HOST: mysql restart: always ports: - "8080:80" php-apache: build: ./php volumes: - ./htdocs:/var/www/html restart: always ports: - "80:80" depends_on: - mysql volumes: db_data: {} 上記のcomposeファイルを例に書き方の説明をします。 version 使用するDocker-composeバージョンを定義する記述です。 現在(2021年5月)は3系が最新のバージョンになります。 バージョンによって、Composeファイルの書き方変わります。 ここでは「3.7」を設定します。 services(サービス) 「サービス」は起動するコンテナーの設定の定義のことです。 ここに各コンテナーと、その設定を記述します。 サービスには名前をつけて複数定義可能で、各サービス間での通信を定義できます。 サービス名 サービスはservicesに定義する各サービスの名前です。 任意で決めることができます。 ここではmysqlとphpmyadmin、php-apacheの3つのサービスを定義します。 このサービスごとにコンテナが起動します。 image(イメージ) イメージはDockerのコンテナーの元になるものです。 もし指定したイメージがなければbuildするときにインストールされます。 ここではmysqlとphpmyadminでそれぞれ、「mysql:5.7」と「phpmyadmin/phpmyadmin」のイメージを使用します。 volumes(ボリューム) ボリュームにはマウントする設定ファイルのパスを指定します。 マウントとは簡単に言うとディレクトリを同期する事です。 restart コンテナの起動に失敗したときの再起動の設定。何もしない場合は「no」を設定。 再起動する場合は「always」を設定する。 environment 環境変数を指定します。 Mysqlのパスワードやデータベース名前などを指定し、ここで設定した環境変数名は実際のプログラムでも使います。 build Dockerファイルを使用してimageを作成するときに、使用するDockerファイルを指定します。 ここで作成したimageを使用してサービスのコンテナを立ち上げます。 ports ポートの設定を設定します。 depends_on ほかのサービスとの依存関係を設定します。 「compose up」と「compose down」したときに指定したサービスが先に起動/終了するようになります。 docker-compose.yml version: "3.7" services: mysql: image: mysql:5.7 volumes: - db_data:/var/lib/mysql restart: always environment: MYSQL_ROOT_PASSWORD: "password" phpmyadmin: depends_on: - mysql image: phpmyadmin/phpmyadmin environment: PMA_HOST: mysql restart: always ports: - "8080:80" php-apache: build: ./php volumes: - ./htdocs:/var/www/html restart: always ports: - "80:80" depends_on: - mysql 「Docker-compose」コマンド一覧 コマンドの説明に関しては、こちらのサイトがわかりやすかったので、説明を引用をしています。 引用元のサイト docker-compose build 「docker-compose build」コマンドは、サービス内のイメージに対してビルドを行います。 イメージがビルドされていないとコンテナを起動できないため、先にイメージのビルドを行います。 サービスのビルド # サービスをビルド $ docker-compose build # サービスをビルド(キャッシュを使わない) $ docker-compose build --no-cache # サービスを指定してビルド $ docker-compose build [サービス名] docker-compose up 「docker-compose up」コマンドは、サービス内のイメージからコンテナを作成して起動します。 すでにコンテナが作成されている場合は、イメージを読み込み直してコンテナを再起動させます。 「-d」オプションをつけることで、バックグラウンドでコンテナを起動させることも可能です。 コンテナの起動 # サービスからコンテナを作成し、起動 $ docker-compose up -d docker-compose logs 「docker-compose logs」コマンドは、コンテナの起動ログを出力します。 コンテナ起動ログを確認 # サービスの起動ログを出力 $ docker-compose logs docker-compose run 「docker-compose run」コマンドは、指定したサービスから新たなコンテナを作成し、コマンドを実行します。 そのため、このコマンドを実行した回数だけコンテナが増えていきます。 コンテナを増やしたくない場合は、「--rm」オプションをつけましょう。 コマンドの実行 docker-compose run --rm app rails db:create docker-compose exe 「docker-compose build」コマンドは、指定したサービスのコンテナにログインします。 コンテナにログイン # コンテナにログイン $ docker-compose exec app bash docker-compose stop 「docker-compose stop」コマンドは、サービス内のコンテナを停止します。 コンテナ停止 # サービスのコンテナを再起動 $ docker-compose restart Restarting docker_web_1 ... done Restarting docker_app_1 ... done Restarting docker_db_1 ... done # 指定したサービスのコンテナを再起動 $ docker-compose restart app Restarting docker_app_1 ... done docker-compose rm 「docker-compose rm」コマンドは、サービス内のコンテナを削除します。 コンテナの削除 # サービスのコンテナを削除 $ docker-compose rm Going to remove docker_web_1, docker_app_1, docker_db_1 Are you sure? [yN] y Removing docker_web_1 ... done Removing docker_app_1 ... done Removing docker_db_1 ... done # 指定したサービスのコンテナを削除 $ docker-compose rm app Going to remove docker_app_1 Are you sure? [yN] y Removing docker_app_1 ... done
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPで配列の中身を再帰的に処理するメモ

引数で渡ってくる配列の深さ・要素数などが分からなくても、上層から下層まで全ての要素に対して処理をしたい!と思った時のメモです。 結論から言えばarray_walk_recursive関数で再帰処理が可能。 どんな処理を適用するかはユーザ定義関数として自分で決められます。 そのユーザ定義関数を静的メソッドとして使う書き方は第二引数で指定しますが、ただの関数であれば関数名を渡してあげるだけでOKです。 使用例 <?php namespace App\Library; use Illuminate\Support\Facades\Log; class LogClass { public static function walkLog($data) { if (!empty($data['results'])){ array_walk_recursive($data['results'], ['self','maskLog']); } } public static function maskLog(&$item, $key) { if (!is_string($item) || mb_strlen($item) <= 8) return; $item = mb_substr($item, 0, 8); return; } } 参考URL array_walk_recursive — 配列の全ての要素に、ユーザー関数を再帰的に適用する PHPの多次元配列で、階層の深さやキー名が毎回異なる場合に、数値または文字列の値のときだけ操作を行うにはどうしたらよいか
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CSVファイルをDBにインポートする

概要 容量の大きいCSVファイルをMySQLに流し込む処理を作ることがあり、調べた内容をメモしておく。 ミドルウェア構成 dockerで Apache + PHP + MadiaDB のローカル環境を作ってテストしてみる。 docker-compose.yml version: '3' services: httpd: image: local/apache:2.4 build: ./docker/httpd/ ports: - '8080:80' volumes: - app:/var/www/www.example.com:cached php: image: local/php:7.3 build: context: ./ dockerfile: ./docker/php/Dockerfile ports: - '9000:9000' volumes: - app:/var/www/www.example.com:cached depends_on: - mariadb mariadb: image: local/mariadb:10.2 build: ./docker/mariadb/ environment: - MYSQL_DATABASE=mysql - MYSQL_ROOT_PASSWORD=password ports: - '3306:3306' volumes: - mariadb-data:/var/lib/mysql volumes: mariadb-data: app: driver_opts: type: none device: ${PWD}/ o: bind アプリケーションについて インポートコマンドを作る際の方針は以下の通りです。 Laravelのコマンドとして構築する 一時テーブルを作って、そちらにデータを一括流し込みする Load Data Local Infileを使ってインポートする ※ PHPで1行ごと読んで処理していると時間かかるため 準備 Laravelで LOAD DATA LOCAL INFILE を使うには、DBとLaravelの設定が必要なようです。 my.cnf [server] local_infile=true config/database.php 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', 'mariadb'), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'example'), 'username' => env('DB_USERNAME', 'mysql'), 'password' => env('DB_PASSWORD', 'password'), 'unix_socket' => env('DB_SOCKET', ''), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => true, 'engine' => null, 'options' => [ PDO::ATTR_EMULATE_PREPARES => true, PDO::MYSQL_ATTR_LOCAL_INFILE => true, // ← ここ!! ], ], 実装してみる まず、コマンドを追加する。 $ php artisan make:command ImportCsv サンプルなので、コマンドにすべて直書きしたいと思います。 <?php namespace App\Console\Commands; use Illuminate\Console\Command; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; class ImportCsv extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'import:csv'; /** * The console command description. * * @var string */ protected $description = 'import data from csv file.'; public function handle() { $this->info('start'); // ファイルを指定する $file = '/var/www/www.example.com/hoge.csv'; // 一時テーブルを作成する $this->createTemporaryTable(); // 一次テーブルにCSVファイルのデータを流し込む $this->importData($file); // その他登録処理などを記述する // ... $this->info('complete'); } private function createTemporaryTable() { Schema::create('tmp_hoge', function (Blueprint $table) { $table->bigIncrements('id')->comment('id'); $table->string('title', 50)->comment('タイトル'); $table->string('description', 255)->comment('説明'); $table->integer('price')->comment('金額'); $table->integer('tax_rate')->comment('税率'); $table->string('memo')->comment('備考'); $table->temporary(); }); } private function importData(string $file) { DB::statement("SET CHARACTER_SET_DATABASE=sjis;"); // エンコード指定 DB::statement(" LOAD DATA LOCAL INFILE ? INTO TABLE `tmp_hoge` FIELDS TERMINATED BY ? ENCLOSED BY ? LINES TERMINATED BY ? (@1,@2,@3,@4,@5,@6,@7,@8,@9,@10,@11,@12,@13,@14,@15,@16,@17,@18,@19,@20,@21,@22,@23,@24,@25,@26,@27,@28,@29,@30) SET title=@1, description=@2, price=@5, tax_rate=@6, memo=@30 ; ", [ $file, ',', '"', "\r\n" ]); } } 実行するときは、こんな感じでしょうか。 $ php artisan import:csv こんな感じでインポートできました。 まとめ ローカルのdocker環境で、800万件のデータを取り込んで見たときは、 だいたい90秒(1分30秒)ぐらいだったでしょうか。 入力検証などはできないかったりするので、その点はデメリットです。 しかし、信頼できるデータをまとめて大量に流し込む場合には便利かもしれませんね。 参考サイト LaravelでLOAD DATA LOCAL INFILEできない Creating Temporary table in laravel/lumen and insert data MySQLの”LOAD DATA INFILE”などでCSVファイル入出力時の文字コード、権限などの注意点 MysqlでCSVから特定列だけインポートする 【MySQL】LOAD DATA INFILE するときのファイルの文字コード 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

php-jwtを使って認証付きAPIの実装方法

php-jwtを使って認証付きAPIの実装方法 概要 ホストAからホストBへの通信で勝手アクセスを防ぐため アクセスキーを持つプログラムだけが実行できるようにする。 php-jwt導入 php-jwtをcomposerを使いダウンロード $ composer require firebase/php-jwt firebase/php-jwt サンプルコード サンプルは最近案件で使っているのでCodeigniterベース?‍♂️ JWT_Test.php JWT::encodeでAPP_SECRET_KEYを使いエンコードを行う JWT::decodeでAPP_SECRET_KEYを使いデコードを行う use Firebase\JWT\JWT; const APP_SECRET_KEY = "xxxxxxxxxx"; class JWT_Test { /** * APP_SECRET_KEYを使い暗号化を行う */ function encode() { $payload = array( "iss" => "xxx", "aud" => "xxx/xxx", "iat" => time(), "exp" => time() + 3600 ); $jwt = JWT::encode($payload, APP_SECRET_KEY); return $jwt; } /** * APP_SECRET_KEYを使い復号化を行う */ function decode($jwt) { $decoded = JWT::decode($jwt, APP_SECRET_KEY, ['HS256']); return $decoded; } } Welcome.php Guzzleを使いAPIリクエストを行うSSL認証のサイトにアクセスする場合、 'verify' => FCPATH.'cacert.pem' が必要、cacert.pemは以下 Authorizationヘッダーにエンコードしたtokenを付けてアクセス リクエスト先はAuthorizationヘッダーを取得デコード可能な場合のみ処理を継続 curl - Extract CA Certs from Mozilla <?php defined('BASEPATH') OR exit('No direct script access allowed'); class Welcome extends CI_Controller { public function __construct() { parent::__construct(); $this->load->model('JWT_Test'); } /** * 本体側 Guzzleを使いヘッダーにトークン付きリクエストを行う */ public function jwt_request() { $token = $this->JWT_Test->encode(); $client = new GuzzleHttp\Client([ 'base_uri' => 'https://xxx.xxx.xxx/', 'verify' => FCPATH.'cacert.pem' ]); $headers = [ 'Authorization' => $token, 'Accept' => 'application/json', ]; $response = $client->request('GET', '/sample-acc/welcome/jwt_request_decode', [ 'headers' => $headers ]); var_dump($response->getBody()->getContents()); } /** * レスポンス側 ヘッダーのトークンが復号化可能な場合に処理を継続 */ public function jwt_request_decode() { $headers = apache_request_headers(); $token = $headers['Authorization']; try { $result = $this->JWT_Test->decode($token); echo json_encode($result); } catch (Exception $e) { echo $e->getMessage(); } } } いいね!と思ったら LGTM お願いします 【PR】週末ハッカソンというイベントやってます! → https://weekend-hackathon.toyscreation.jp/about/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Docker × Laravel】browsersyncの設定方法

環境 docker 20.10.6 docker-compose 1.29.1 composer 2.0.14 laravel 8.42.1 laravel Mix 6.0.19 php 8.0.7 browser-sync 2.26.14 手順 browser-syncをインストール webpack.mix.jsを編集 起動 browser-syncをインストール 以下のコマンドでグローバルにbrowser-syncをインストール(今後使わなそうなら開発環境のみでもOK) zsh npm install -g browser-sync browser-sync-webpack-plugin webpack.mix.jsを編集 webpack.mix.js mix.js("resources/js/app.js", "public/js") .postCss("resources/css/app.css", "public/css", [ require("postcss-import"), require("tailwindcss"), require("autoprefixer"), ]) // ここから下 .browserSync({ proxy: { target: "http://localhost:10080", }, files: ["./resources/**/*", "./public/**/*"], open: true, reloadOnRestart: true, }); webpack.mix.js proxy: { target: "http://localhost:10080", }, targetにはwebサーバのホスト側ポート番号を設定 docker-compose.yml version: "3.9" services:         (省略) web: image: nginx:1.20-alpine ports: - 10080:80 //ここの左側の値のこと volumes: - ./backend:/work - ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf working_dir: /work         (省略) webpack.mix.js files: ["./resources/**/*", "./public/**/*"], ここで、各自変更を検知してほしいディレクトリを設定 今回で言うとresources、public配下のファイルに編集が加わるたびにブラウザに自動で反映してくれる webpack.mix.js open: true, これはbrowser-syncを起動したときに自動でブラウザを開くかの設定 tureにすると自動で開く webpack.mix.js reloadOnRestart: true, これはbrowser-syncを再起動したときに、開いているブラウザをリロードするかの設定 動作確認 laravelのプロジェクトルートで以下のコマンドを打つと npm run watch browser-syncが起動して (open: trueにしていれば)http://localhost:3000でブラウザが開かれます
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Docker × Laravel】browser-syncの設定方法

環境 docker 20.10.6 docker-compose 1.29.1 composer 2.0.14 laravel 8.42.1 laravel Mix 6.0.19 php 8.0.7 browser-sync 2.26.14 手順 browser-syncをインストール webpack.mix.jsを編集 起動 browser-syncをインストール 以下のコマンドでグローバルにbrowser-syncをインストール(今後使わなそうなら開発環境のみでもOK) zsh npm install -g browser-sync browser-sync-webpack-plugin webpack.mix.jsを編集 webpack.mix.js mix.js("resources/js/app.js", "public/js") .postCss("resources/css/app.css", "public/css", [ require("postcss-import"), require("tailwindcss"), require("autoprefixer"), ]) // ここから下 .browserSync({ proxy: { target: "http://localhost:10080", }, files: ["./resources/**/*", "./public/**/*"], open: true, reloadOnRestart: true, }); webpack.mix.js proxy: { target: "http://localhost:10080", }, targetにはwebサーバのホスト側ポート番号を設定 docker-compose.yml version: "3.9" services:         (省略) web: image: nginx:1.20-alpine ports: - 10080:80 //ここの左側の値のこと volumes: - ./backend:/work - ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf working_dir: /work         (省略) webpack.mix.js files: ["./resources/**/*", "./public/**/*"], ここで、各自変更を検知してほしいディレクトリを設定 今回で言うとresources、public配下のファイルに編集が加わるたびにブラウザに自動で反映してくれる webpack.mix.js open: true, これはbrowser-syncを起動したときに自動でブラウザを開くかの設定 tureにすると自動で開く webpack.mix.js reloadOnRestart: true, これはbrowser-syncを再起動したときに、開いているブラウザをリロードするかの設定 動作確認 laravelのプロジェクトルートで以下のコマンドを打つと npm run watch browser-syncが起動して (open: trueにしていれば)http://localhost:3000でブラウザが開かれます
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP基礎メモその2

クラス オブジェクト指向では、クラスというオブジェクトの設計図を作成し、クラス(設計図)を元にオブジェクトを生成する。 <?php // クラス名は大文字で始める class Menu { // クラスの内容を書く } ?> インスタンス クラスを元に作成された実体を『インスタンス』と呼ぶ。インスタンスとオブジェクトは同じ意味で使われる。 インスタンスは『new』を用いて『new クラス名()』で生成できる。 『$変数名 = new クラス名()』のようにすることで生成したインスタンスを変数に代入している。 <?php class Menu { // クラスの内容 } $menu1 = new Menu(); $menu2 = new Menu(); $menu3 = new Menu(); ?> プロパティ プロパティはインスタンスが持つデータのことで『public $プロパティ名』のように定義する。 <?php class Menu { public $name; // nameプロパティを定義 } ?> 『インスタンス->プロパティ名』とすることでそのインスタンスのプロパティにアクセスすることができる(プロパティ名に$は不要)。 <?php class Menu { public $name; // nameプロパティを定義 } $curry = new Menu(); $curry->name = 'CURRY'; // プロパティに値をセット echo $curry->name; // セットした値にアクセス ?> 結果 CURRY メソッド メソッドはそれぞれのインスタンスが持つ関数のことである。 メソッドは『pablic function メソッド名()』のように定義する。また、『インスタンス->メソッド名()』のようにメソッドを呼び出すことができる。 <?php class Menu { // メソッドを定義 public function hello() { echo 'これはMenuインスタンスです'; } } $curry = new Menu(); $curry->hello(); ?> 結果 これはMenuインスタンスです $this メソッド内でインスタンスのプロパティやメソッドにアクセスしたい時には『$this』という特殊な変数を用いる。 $thisはクラス内のメソッドの定義の中でのみ使用できる。 $thisはメソッドが呼ばれた時に、そのメソッドを呼び出しているインスタンスに置き換えられる。 <?php class Menu { public $name; public function hello() { echo 'これは'.$this->name.'です'; // この場合$thisはメソッドを呼び出している$curryインスタンスに置き換えられる } } $curry = new Menu(); $curry->name = 'CURRY'; $curry->hello(); 結果 これはCURRYです コンストラクタ __constructメソッドを定義すると、newを用いてインスタンスを生成するときに、このメソッドが自動的に呼び出される。 このようなインスタンス生成時に呼び出されるメソッドをコンストラクタという。 class Menu { public function __construct() { // コンストラクタ内で行いたい処理 } } <?php class Menu { public function __construct() { echo 'メニューが作成されました'; } } // コンストラクタが呼び出される $curry = new Menu(); 結果 メニューが作成されました コンストラクタと引数 newを用いてインスタンスを生成する際に引数を与えることができ、その値を__constructメソッドに渡せる。 <?php class Menu { public $name; public function __construct($name) { echo $name; // 引数を受け取る } } $curry = new Menu('CURRY'); // 引数を与える 結果 CURRY コンストラクタとプロパティ __constructメソッド内で、$thisを用いてインスタンスのプロパティに値をセットすることができる。 class Menu { public $name; public function __construct($name) { $this->name = $name; } } $curry = new Menu('CURRY'); echo $curry->name; 結果 CURRY HTMLにPHPを埋め込む HTMLのコードとPHPのコードを切り分けることができ、見やすくなる。 // PHPのコード <?php class Menu { public $name; } $curry = new Menu('CURRY'); ?> // HTMLのコード <p><?php echo $curry->name ?></p> セミコロンを省略できる条件 PHPのコードは、1行で書き表す場合は、区切る必要がないため、セミコロンを省略できる。 <p><?php echo $curry->name ?></p> foreach文をHTMLに埋め込む foreach文の 『{』 の代わりに 『:』、 『}』 の代わりに 『endforeach』 と記述し、その間に処理を書いていく。 通常のforeach文 <?php $words = array('apple', 'banana', 'orange'); foreach ($words as $eord) { echo '<p>'.$word.'</p>'; } ?> endforeachを用いたforeach文 <?php $words = array('apple', 'banana', 'orange'); ?> <?php foreach ($words as $word): ?> <p><?php echo $word ?></p> <?php endforeach ?> endif endfor if文、for文、while文やswitch文などもforeach文と同様に、それぞれ『endif』、『endfor』、『endwhile』、『endswitch』を使って書くことができる。 endifを用いたif文 <?php $age = 24; ?> <?php if($age >= 20): ?> <p><?php echo 'あなたは成人です' ?></p> <?php endif ?> endforを用いたfor文 <?php for($i = 0; $i < 100; $i++): ?> <p><?php echo $i ?></p> <?php endfor ?> require_once 『require_once』を用いると別のphpファイルを読み込むことができる。 require_onceで読み込んだファイルで定義されているクラスや変数を、require_onceを記述したファイル内で使うことができる。 menu.php <?php class Menu { // } ?> data.php <?php require_once('menu.php'); // menu.phpを読み込み $curry = new Menu('CURRY') // data.phpで定義した変数を使える ?> index.php <?php require_once('data.php') ?> // data.phpを読み込み <p> <?php echo $curry->name ?> // data.phpで定義した変数が使える </p> 画像を表示する imageプロパティの値はタグのsrc属性に指定するため、ダブルクォーテーションの中に埋め込む。 echoしないとphpの処理の結果が出力されず、HTMLに反映されないので注意する。 <img src="<?php echo $curry->image ?>" > publicとprivate カプセル化とは、クラスのプロパティとメソッドへのアクセスを制限することをいう。 クラスの外部からアクセスできるようにするには『public』を、アクセスできないようにするには『private』を用いて定義する。 プロパティは基本的にアクセス権をprivateにする。 public class Menu { // public public $name; public function __construct($name) { $this->name = $name; } } $curry = new Menu('CURRY'); echo $curry->name; 結果 CURRY private class Menu { // private private $name; public function __construct($name) { $this->name = $name; } } $curry = new Menu('CURRY'); echo $curry->name; 結果  エラー ゲッター プロパティのアクセス権をprivateにするとプロパティの値をクラスの外から取り出すことができなくなる。そこで、 プロパティの値を返すだけのメソッドを定義する。このような、プロパティの値を返すだけのメソッドをゲッターという。 ゲッターは『getプロパティ名』のように命名するのが一般的である。 class Menu { private $name; public function __construct($name) { $this->name = $name; // クラス内なのでアクセスできる } // ゲッターの定義 public function getName() { return $this->name; // クラス内なのでアクセスできる } } $curry = new Menu('CURRY'); echo $curry->getName(); セッター プロパティのアクセス権をprivateにするとプロパティの値をクラスの外から変更できなくなる。そこで、 プロパティの値を変更するメソッドを定義する。このような、プロパティの値を変更するメソッドをセッターという。 セッターは『setプロパティー名』のように命名するのが一般的である。 class Menu { private $orderCount; // ・・・ // セッターの定義 public function setOrderCount($orderCount) { $this->orderCount = $orderCount; } } $juice = new Menu('JUICE',・・・); // セッターを用いて値をセット $juice->setOrderCount(2); echo $juice->getOrderCount(); // 結果 : 2 $juice->orderCount = 4; // privateなので直接アクセスできない プロパティの初期値 プロパティを定義する際に初期値をセットすることができる。 class Menu { // 初期値をセット private $orderCount = 0; public function getOrderCount() { return $this->orderCount; } } $curry = new Menu('CURRY'); echo $curry->getOrderCount(); 結果 0
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む