- 投稿日:2020-10-25T22:51:10+09:00
laravelでモデルのリレーションで検索機能をつくってみた件
今回したかったこと
社員の出張管理アプリを作成しており、社員がどの場所に出張したか履歴を記録する処理を実装したところ、履歴の検索機能を作りたくなった。
Members(社員)--(1対多)--Histories(履歴)(中間テーブルに相当)--(多対1)--Locations(出張先)
テーブルには上記のようなリレーションが設定されており、この3つのモデルから、Historiesを軸に検索機能を実装したいことがあり挑戦したところ、大変勉強になったのでメモ。
処理の条件
・出張日、社員名、行先場所名で検索する
・3つのうち、リクエスト項目数は0~3でも、どんな組み合わせでも検索できるテーブル詳細
Historiesというテーブルが中間テーブルに相当します。
ポイントとなる点は、リレーションできるようにHistoriesにはmember_id、location_idというカラムを持っている。・Membersテーブル +------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | user_id | int(10) unsigned | YES | | NULL | | | name | varchar(255) | YES | | NULL | | | address | varchar(255) | YES | | NULL | | | latitude | double(9,6) | YES | | NULL | | | longitude | double(9,6) | YES | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | +------------+------------------+------+-----+---------+----------------+ ・Historiesテーブル +-------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | trip_date | date | NO | | NULL | | | member_id | int(10) unsigned | NO | MUL | NULL | | | start | int(11) | NO | | NULL | | | location_id | int(10) unsigned | NO | MUL | NULL | | | distance | int(11) | YES | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | | deleted_at | timestamp | YES | | NULL | | +-------------+------------------+------+-----+---------+---------------- ・Locationsテーブル +------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | | NULL | | | address | varchar(255) | YES | | NULL | | | latitude | double(9,6) | YES | | NULL | | | longitude | double(9,6) | YES | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | +------------+------------------+------+-----+---------+----------------+モデルにリレーションを定義
モデルのクラス内に、それぞれ下記のリレーションを定義します。
Memberモデル public function history(){ return $this->hasMany(History::class); } Historyモデル public function member(){ return $this->belongsTo(Member::class); } public function location(){ return $this->belongsTo(Location::class); } Locationモデル public function history(){ return $this->hasMany(History::class); }ビューへフォームを作成して、ルーティングを通す
Historyの一覧を表示するビューに検索フォームを作る。
リクエストすると、Historyコントローラーを通るようにルーティング処理を書いておく。フォーム {{ Form::open(['method' => 'GET', 'route' => 'history.list']) }} {{ Form::input('date', 'trip_date', null, ['placeHolder' => '日付']) }} {{ Form::input('text', 'member_name', null, ['placeHolder' => '名前']) }} {{ Form::input('text', 'location_name', null, ['placeHolder' => '目的地']) }} {{ Form::submit('検索', array('class' => 'btn btn-primary')) }} {{ Form::close() }}ルーティング Route::group(['prefix' => '/history', 'as' => 'history.'], function() { Route::get('/', [ 'as' => 'list', 'uses' => 'HistoryController@index' ]); });出来上がった処理
飛んできたリクエストを配列で拾い、リクエストあれば検索、なければ全件でif分岐する。
というような感じになっています。今回のポイントになった点は下記の通り。whenメソッド・・・第1引数がtrueなら、第2引数のクロージャーを処理するようになっている。
本例の場合、出張日がリクエストされていれば、SQLでwhere文を付加するようになっている。whereHasメソッド・・・第1引数にモデルに定義したリレーションのメソッド名を渡すと、第2引数のクロージャーを処理するようになっている。本例の場合、出張先名、社員名がリクエストされていれば、SQLでwhere~like文を付加するようになっている。
public function index(Request $request) { $title = 'History List'; $search = array( 'trip_date' => $request->get('trip_date'), 'member_name' => $request->get('member_name'), 'location_name' => $request->get('location_name') ); if(array_filter($search)){ $trip_date = $search['trip_date']; //////////今回作った処理///////// $histories = History::when($trip_date, function($query, $trip_date){ return $query->where('trip_date', $trip_date); }) ->whereHas('location',function($q) use ($search) {$q->where('name', 'like', '%'.$search['location_name'].'%');}) ->whereHas('member',function($q) use ($search) {$q->where('name', 'like', '%'.$search['member_name'].'%');}) ->get(); ////////////////////////////// }else{ $histories = History::all(); } return view('history/index', ['title' => $title], ['histories' => $histories]); }ちなみに発行されたクエリを確認すると以下のようになっていました。
exists句が使われており、続く()内のサブクエリでレコードが存在しているか見ています。
サブクエリはwhere~likeで実行されているので空白でリクエストしても抽出できるわけですね。select * from `histories` where `trip_date` = '2020-09-30' and exists (select * from `locations` where `histories`.`location_id` = `locations`.`id` and `name` like '%%') and exists (select * from `members` where `histories`.`member_id` = `members`.`id` and `name` like '%佐々木%')
- 投稿日:2020-10-25T21:32:27+09:00
Laravel:DB(Sequel Pro)をつなげてmigrateする方法
【概要】
1.結論
2.どのように記載するか
3.開発環境
補足
1.結論
create databaseしたファイル名と.envとconfig/database.phpでのファイル名等を一致させ、php artisan make:migrationしてから必要な記述をしphp artisan migrateする!
2.どのように記載するか
MysqlやSequel Proの導入は省きます。
❶まずデータベースを作成します
$ mysql -u root -p #パスワードを何も設定してなければEnterで進めます。 mysql> create database ファイル名;❷.env.exampleとconfig/database.phpでDB(Sequel Pro)をつなげます。
.envDB_CONNECTION=mysql #mysqlを使用しているのでこのように記載します。 DB_HOST=127.0.0.1 #データーベースサーバーのホストの指定です。 DB_PORT=3306 #初期値がこの値なのでいじらないでOKです。そうでない場合はこの値にしてください。 DB_DATABASE= #create database ファイル名;のファイル名を記載してください。 DB_USERNAME=root #Sequel Proのユーザー名を記載してください。 DB_PASSWORD= #パスワードを設定していなければ空欄でOKです。config/database.php'mysql' => [ 'driver' => 'mysql', 'url' => env('DATABASE_URL'), 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', ''), 'username' => env('DB_USERNAME', 'root'), # .env.example と一致させます。❸php artisan make:migrationして、php artisan migrateします。
$ php artisan make:migration ファイル名php artisan make:migration ファイル名を記載するとファイルが生成されるので必要なカラム名や型を設定します。
#省略 public function up() { Schema::create('people', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('mail'); $table->integer('age'); $table->timestamps(); }); } #省略ここでphp artisan migrateするとcreate databaseしたDBのファイル名に反映されます。
参考にさせていただいたURL:
❶DBに反映:LaravelでSequel Proを使ったMySQLのデータベース接続(Mac/初心者向き)
3.開発環境
PHP 7.4.10
Laravel 8.9
Apache 2.4.41
Mysql 5.6.47
Sequl Pro 1.1.2
補足
しかし、2.❸でphp artisan migrateしたところ、エラーが生じ、SQLSTATE[HY000] [1049] Unknown database 'laravel'とでました。
結論から言うと、"DB_DATABASE=Laravel"のデフォルトの記述のままでした。その場所はどこかというと、下記で入力した部分でした。
$ vi .envここに記載してあるDB_DATABASE= が"Laravel"のままで自分が.env.exampleで設定した名前と違っていたので直したところDBに反映できました。自分が記載した.envファイルは.env.exampleファイルに記載しており"vi .env"に記載していなかったためです。
参考にさせていただいたURL:
❷エラーについて:Laravel MySQL SQLSTATE[HY000] [1049] Unknown database が出た時に確認すること
- 投稿日:2020-10-25T18:49:32+09:00
LaravelのStorageでプロジェクト外のローカルディスク上にファイルを出力する
行うこと
Windows10のxampp上で立ち上げたLaravel8.4.0のプロジェクトで、textファイルをLaravelプロジェクトフォルダ外であるD:/testに出力する。
方法
config/filesystems.phpを編集して、Storageのdiskに新たに保存先を加える。
config/filesystems.php'disks' => [ 'local' => [ 'driver' => 'local', 'root' => storage_path('app'), ], /* -----付け加え箇所----- */ 'local_test' => [ 'driver' => 'local', 'root' => 'D:/test', ], /* ---------- */ 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), 'url' => env('APP_URL').'/storage', 'visibility' => 'public', ], 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'url' => env('AWS_URL'), 'endpoint' => env('AWS_ENDPOINT'), ], ],ファイルを出力したいタイミングで記述する
app/Http/Controllers/PostController.phpStorage::disk('local_test')->put($file_name, $file_content);参考
stack overflow: Adding Custom path for storage in filesystems.php Laravel
LaravelでStorageを使ったファイルおよびディレクトリの操作をまとめてみた
Laravel File Storage(英語)
Laravel File Storage(日本語)
- 投稿日:2020-10-25T10:58:27+09:00
Mac PhpRedis PHP拡張をインストールする
目的
- PhpRedisのインストール方法とアンインストール方法をまとめる
実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.5) ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) プロセッサ 2 GHz クアッドコアIntel Core i5 メモリ 32 GB 3733 MHz LPDDR4 グラフィックス Intel Iris Plus Graphics 1536 MB 概要
- インストール
- アンインストール
詳細
インストール
下記コマンドを実行してインストールを実施する。
$ pecl install redisインストール中に下記を問われるが基本何も入力せずにEnterを押下する。
enable igbinary serializer support? [no] : enable lzf compression support? [no] : enable zstd compression support? [no] :
下記の4行が表示されればインストールは完了しPhpRedis PHP拡張が有効になっている。
Build process completed successfully Installing '/usr/local/Cellar/php/7.4.11/pecl/20190902/redis.so' install ok: channel://pecl.php.net/redis-5.3.1 Extension redis enabled in php.ini
アンインストール
アンインストールしたいときは下記を実行する。
$ pecl uninstall redis下記の二行が出力されればアンインストールは完了したことになる。
xtension redis disabled in php.ini uninstall ok: channel://pecl.php.net/redis-5.3.1
下記コマンドを実行してphp.iniファイルの場所を出力する。
$ php -r "echo phpinfo();" | grep "php.ini"下記コマンドを実行してphp.iniファイルのバックアップを作成する。(下記コマンドは筆者の環境でのphp.iniファイルの場所を指しており、皆さんは先のコマンドで出力されたphp.iniのパスを指定してバックアップを作成していただきたい。)
cp /usr/local/etc/php/7.4/php.ini /usr/local/etc/php/7.4/php.ini_org
下記コマンドを実行してphp.iniファイルを開く。(下記コマンドは筆者の環境でのphp.iniファイルの場所を指しており、皆さんは先のコマンドで出力されたphp.iniのパスを指定してファイルを開いていただきたい。)
$ vi /usr/local/etc/php/7.4/php.iniファイルに下記の記載があるはずなのでコメントアウト後に保存する。
修正前
/usr/local/etc/php/7.4/php.iniextension="redis.so"修正後
/usr/local/etc/php/7.4/php.ini;extension="redis.so"
- 投稿日:2020-10-25T04:50:46+09:00
Laravel 【初心者】 PHPフレームワーク Laravel入門 第2版 の内容を一部図式化してみた件 ⑰バリデーションルール
はじめに
この記事はプログラミングをはじめたばかりの素人が、メモするのに利用しています。
内容には誤りがあるかもしれません。PHPフレームワーク Laravel入門 第2版 について
Laravelを独学で学ぶにあたり、評判がよかったのでAmazon Kindleで購入しました。
内容も色々な方法が書いてあるので、Laravelをはじめて勉強される方にはおすすめの本です。Amazon リンク
https://www.amazon.co.jp/gp/product/B08625YD7H/ref=ppx_yo_dt_b_d_asin_image_o09?ie=UTF8&psc=1図式化について
学習中に、文字だけだとイメージが掴みづらかったので、自分の為に作成したものです。
また方法が色々あり過ぎて混乱したので、整理する意味もあります。
ディレクトリを覚えるのにも使いました。コードは必要なものだけ抜粋しているので、全てではありません。
これだけ見ても勉強にはならないので、PHPフレームワーク Laravel入門 第2版を是非購入してみて下さい。※ちなみに著者の方とは何の関係もありません。
素人が作成した物なので、参考程度に使ってください。
バリデーションルール
- 投稿日:2020-10-25T04:47:51+09:00
Laravel 【初心者】 PHPフレームワーク Laravel入門 第2版 の内容を一部図式化してみた件 ⑯オリジナルバリデータ
はじめに
この記事はプログラミングをはじめたばかりの素人が、メモするのに利用しています。
内容には誤りがあるかもしれません。PHPフレームワーク Laravel入門 第2版 について
Laravelを独学で学ぶにあたり、評判がよかったのでAmazon Kindleで購入しました。
内容も色々な方法が書いてあるので、Laravelをはじめて勉強される方にはおすすめの本です。Amazon リンク
https://www.amazon.co.jp/gp/product/B08625YD7H/ref=ppx_yo_dt_b_d_asin_image_o09?ie=UTF8&psc=1図式化について
学習中に、文字だけだとイメージが掴みづらかったので、自分の為に作成したものです。
また方法が色々あり過ぎて混乱したので、整理する意味もあります。
ディレクトリを覚えるのにも使いました。コードは必要なものだけ抜粋しているので、全てではありません。
これだけ見ても勉強にはならないので、PHPフレームワーク Laravel入門 第2版を是非購入してみて下さい。※ちなみに著者の方とは何の関係もありません。
素人が作成した物なので、参考程度に使ってください。
オリジナルバリデータ
- 投稿日:2020-10-25T04:44:18+09:00
Laravel 【初心者】 PHPフレームワーク Laravel入門 第2版 の内容を一部図式化してみた件 ⑮オリジナルバリデータ
はじめに
この記事はプログラミングをはじめたばかりの素人が、メモするのに利用しています。
内容には誤りがあるかもしれません。PHPフレームワーク Laravel入門 第2版 について
Laravelを独学で学ぶにあたり、評判がよかったのでAmazon Kindleで購入しました。
内容も色々な方法が書いてあるので、Laravelをはじめて勉強される方にはおすすめの本です。Amazon リンク
https://www.amazon.co.jp/gp/product/B08625YD7H/ref=ppx_yo_dt_b_d_asin_image_o09?ie=UTF8&psc=1図式化について
学習中に、文字だけだとイメージが掴みづらかったので、自分の為に作成したものです。
また方法が色々あり過ぎて混乱したので、整理する意味もあります。
ディレクトリを覚えるのにも使いました。コードは必要なものだけ抜粋しているので、全てではありません。
これだけ見ても勉強にはならないので、PHPフレームワーク Laravel入門 第2版を是非購入してみて下さい。※ちなみに著者の方とは何の関係もありません。
素人が作成した物なので、参考程度に使ってください。
オリジナルバリデータ
- 投稿日:2020-10-25T01:25:57+09:00
【Laravel】Redis::pipelineが爆速だったメモ
最近業務の一貫で他エンジニアのコードレビューをしている時にコメントした件を備忘録として残しておく。
自身が所属するプロジェクトではPHP×Laravelを使用していて、今回以下のような実装要件であった。
複数のリスト型キーをRedisにセットする
そして以下のような実装がされていた。
foreach($key_map as $key => $value) { Redis::lpush($key, $value); Redis::expire($key, self::CACHE_EXPIRE_TIME); }普通に動くと思うし、パフォーマンスを気にする必要ないのなら、スルーでもいいですが、せっかくなので、
Laravelの公式ドキュメント( https://readouble.com/laravel/5.4/ja/redis.html )をみると、こんなコマンドがあるよと。パイプラインコマンド
一回の操作でサーバに対し多くのコマンドを送る必要がある場合はパイプラインを使うべきでしょう。pipelineメソッドは引数をひとつだけ取り、Redisインスタンスを取る「クロージャ」です。このRedisインスタンスで全コマンドを発行し、一回の操作で全部実行できます。Redis::pipeline(function ($pipe) { for ($i = 0; $i < 1000; $i++) { $pipe->set("key:$i", $i); } });ふむふむ、インスタンス内で全コマンドを発行して一回の操作で全部実行とな。
それは確かにパフォーマンス良さそう。ということで適当にartisanコマンド作って試してみる。
こんな感じ// 直接 $startTime = microtime(true); for ($i = 0; $i < $count; $i++) { Redis::lpush('test_key' . $i, 'hoge'); } $runningTime = microtime(true) - $startTime; var_dump('straight running time: ' . $runningTime . ' [s]'); // pipeline $startTime = microtime(true); $redis = Redis::connection('redis_0'); $redis->pipeline(function ($pipe) use($count) { for ($i = 0; $i < $count; $i++) { $pipe->lpush('test_key_pipeline' . $i, 'hoge'); } }); $runningTime = microtime(true) - $startTime; var_dump('pipeline running time: ' . $runningTime . ' [s]');↓↓結果↓↓
// 10件一括 # php artisan redis_test 10 string(42) "straight running time: 0.0122230052948 [s]" string(46) "pipeline running time: 0.00022292137145996 [s]" // 1000件一括 # php artisan redis_test 1000 string(43) "straight running time: 0.15764403343201 [s]" string(45) "pipeline running time: 0.0015499591827393 [s]"パフォーマンス的には100倍くらいでてる。使わない理由はなさそう。
setとかはアトミックにそもそもできるから、使う必要はないだろうけど、
リスト型やハッシュ型とかで複数一括実行が必要になった際は使えそうです。注意なのが、Redis複数台ある時は、全部に同じコマンドが発行されて無駄が出たりするので、
手前でconnectionごとにグルーピングして、それぞれのRedisサーバーに対して適切なキーリストを渡してあげることかなと思います。これからも勉学に励みます。
- 投稿日:2020-10-25T00:14:22+09:00
Laravel で DROP TABLE したテーブルを再び migrate する方法
一度 DROP TABLE したテーブルを再び migrate する
DROP TABLE は表構造ごと完全削除します。そのため,再びテーブルが必要になった場合はもう一度 migrate を実行する必要があります。
しかし,そのままでは migrate できないので以下に対処法を紹介します。
試しに DROP TABLE してみる。
mysql> USE mydb; mysql> DROP TABLE my_table;テーブルが消えた状態で migrate を実行しようとすると,migrate するものがないと言われてしまいます。
> php artisan migrate Nothing to migrate.migrations テーブルから, DROP したテーブルの id を確認します。
mysql> SELECT * FROM migrations;該当するレコードを削除します。
Laravelでは migrations テーブルでマイグレーションの実行履歴を管理しています。
既にテーブルを削除していても,この履歴を消さない限り migrate は実行されません。mysql> DELETE FROM migrations WHERE id='20';履歴を削除したので migrate を実行します。
> php artisan migrate無事にテーブルが作成されました。