- 投稿日:2020-03-21T22:20:38+09:00
LravelのDBテーブル作成&データ投入
お約束事
本記事で発信される情報は、正確性、完全性、有用性、その他の事項について一切責任を負いかねます。自己判断にてご活用ください。
全体の流れ
Migrationを行いテーブルを作成する
- migrationコマンドを実行するための空ファイルの作成
- 作成されたmigrationファイルをテーブル構造通りに記述する
- migrateコマンドで実際にテーブルを作成する
- 型変更、カラム追加・削除等
Sheederを用いてテーブルデータを投入する
- seedを実行するための空ファイルの作成
- 作成されたseedファイルを投入したいデータに合わせて記述する
- seedコマンドで実際にテーブルにデータを投入する
Migrationでテーブルを作成する
1、migrationするための空ファイルの作成
ターミナルでの作業$php artisan make:migration {テーブル名} Created Migration: {年}_{月}_{日}_{時間}_{テーブル名}{プロジェクトRoot}/database/migrationsにファイルが作成される。
2、作成されたmigrationファイルをテーブル構造通りに記述する
作成されたmigrationファイル<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AppSystem extends Migration { /** * Run the migrations. * * @return void */ public function up() { //{}は不要 Schema::create('{テーブル名}', function (Blueprint $table) { $table->bigIncrements('id');//インクリメントするbigInt $table->boolean('is_maintenance');//bool型 $table->text('app_version');//text $table->dateTime("created_at");//dateTime $table->dateTime("updated_at");//dateTime }); } /** * Reverse the migrations. * * @return void */ public function down() { //{}は不要 Schema::dropIfExists('{テーブル名}'); } }3、migrateコマンドで実際にテーブルを作成する
ターミナルでの作業$php artisan migrate Migrating: 2020_03_21_115544_app_system Migrated: 2020_03_21_115544_app_system (0.05 seconds)※Dockerの場合は、workspaceにログインして実行しないとErrorになります。
作業後にDBにテーブルが作成されていること、migrationsテーブルに実行履歴が登録されていることが確認できる。
4、型変更、カラム追加・削除等
型変更
ターミナルでの作業php artisan make:migration change_{対象カラム名}_{テーブル名}_table --table={テーブル名} Created Migration: 2020_03_21_141300_xxxxxxxxxxxxxxx※何をしたかわかりやすい名前がよいと思います。
migrateファイルを修正public function up() { Schema::table('{テーブル名}', function (Blueprint $table) { //新しい型 $table->text('{カラム名}')->change(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('{テーブル名}', function (Blueprint $table) { //変更前の型 $table->string('{カラム名}')->change(); }); }カラム追加、削除
ターミナルでの作業php artisan make:migration add_{対象カラム名}_{テーブル名}_table --table={テーブル名} Created Migration: 2020_03_21_141300_xxxxxxxxxxxxxxx※何をしたかわかりやすい名前がよいと思います。
migrateファイルを修正(削除はupとdown内の動作を入れ替えればOK)public function up() { Schema::table('{テーブル名}', function (Blueprint $table) { //カラム追加 afterで指定したカラムの後ろに追加できる $table->string('{カラム名}')->after('id'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('{テーブル名}', function (Blueprint $table) { //カラム削除 $table->dropColumn('{カラム名}'); }); }Seederでのデータ投入
1、seedを実行するための空ファイルの作成
ターミナルでの作業$php artisan make:seeder {ファイル名} Seeder created successfully.{プロジェクトRoot}/database/seedsにファイルが作成される
ターミナルでの作業$php composer dump-autoload Discovered Package: beyondcode/laravel-dump-server Discovered Package: encore/laravel-admin Discovered Package: fideloper/proxy Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully.2、 作成されたseedファイルを投入したいデータに合わせて記述する
作成されたseederファイルを修正<?php use Illuminate\Database\Seeder; use App\Libs\Common; class LevelSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { //Jsonファイルを読み込みdecodeしてくれる自作クラスを呼び出す。 //SpreadSheetで作成したマスターデータをJson化したデータが配置してある。 $dataArray = Common::getJsonFile('{jsonFile名}'); //Jsonデータを1件ずつ挿入 foreach ($dataArray as $value){ $insertData = array(); foreach ($value as $key => $data) { $insertData[$key] = $data; } DB::table('level')->insert([ $insertData ]); } } }Jsonファイルを読み込みdecodeするクラス<?php namespace app\Libs; class Common { public static function getJsonFile($fileName){ $url = public_path() . '/json/'.$fileName.'.json'; $json = file_get_contents($url); $json = mb_convert_encoding($json, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN'); $arr = json_decode($json, true); return $arr; } }※ファイルの存在チェックとかはしておりません。必要であればしてください。
※本番環境にjsonファイルはアップしないので、このまま本番にアップするとマスターデータ丸見え等
セキュリティ的に良くないので.gitignore等で除外してください。3、seedコマンドで実際にテーブルにデータを投入する
ターミナルでの作業(任意のテーブルに実行)$php artisan db:seed --class={1で作成したクラスを指定} Database seeding completed successfully.※数百件レベルのデータなら秒で終わるので負荷とかは検討していません。
- 投稿日:2020-03-21T18:59:54+09:00
【fullcalendar】Laravelとfullcalendar(v4)を使って予約管理システムを作った話【Laravel】
仮想案件
家庭教師予約システムをつくる仕様概要
・家庭教師派遣会社は先生(学生バイト達)をAdmin管理画面からスケジューリング
・会員(生徒)は任意の先生を管理画面から予約
・派遣会社の画面と会員の画面はそれぞれ別の認証経路(マルチAuth)
・UIはGoogleカレンダー風を希望前提
・fullcalendarメインの記事になります
・Laravelの基本を理解している方向けです
・非同期通信の基本も理解している方向けです
・データベース設計の基本も理解している方向けです
要はfullcalendarにポイントを置いてます。目次
- 完成イメージ
- LaravelのマルチAuth対応
- 管理画面の作成
- fullcalendarの組み込み
- ビジネスロジックの実装
- 補足
1.完成イメージ
家庭教師派遣会社が使用するAdmin管理画面(週表示)
カレンダー左横の先生の箱をカレンダーにドロップしてイベントを作成
会員(生徒)が使用する予約画面(週表示)
任意のイベント(先生)をクリックして予約処理をする
2.LaravelのマルチAuth対応
派遣会社と会員(生徒)はそれぞれのログイン認証を経ることになるのでその対応です。
既存のguardとproviderに加え、admin用のguardとproviderを作成します。
このあたりは良記事が既に存在するのでそちらを参照してください
(いきなり参照ですいません;)Laravel6 備忘録 −ユーザー認証(Auth)−
【Laravel】マルチログイン(ユーザーと管理者)機能3.管理画面の作成
左にメニュー、右にコンテンツエリアがあるオーソドックスなレイアウトです。
組み方はこちらを参照してください。
(またまた参照ですいません…しかも手前味噌で;)【Laravel】デフォルトの管理画面に左メニューをサクッと設置
4.fullcalendarの組み込み
まずadminから。今回はadmin用のテンプレにてCDNで読み込んでます。
(月表示、週表示、デイリー、リスト、日本語対応)
fullcalendar V3だとapp.jsとぶつかるらしいですが、V4では特に問題ありませんでした。※admin用の外枠テンプレートをadmin_app.blade.phpとして作った場合
/resources/views/layouts/admin_app.blade.php<link href='https://unpkg.com/@fullcalendar/core@4.3.1/main.min.css' rel='stylesheet' /> <link href='https://unpkg.com/@fullcalendar/daygrid@4.3.0/main.min.css' rel='stylesheet' /> <link href='https://unpkg.com/@fullcalendar/timegrid@4.3.0/main.min.css' rel='stylesheet' /> <link href='https://unpkg.com/@fullcalendar/list@4.3.0/main.min.css' rel='stylesheet' /> <script src='https://unpkg.com/@fullcalendar/core@4.3.1/main.min.js'></script> <script src='https://unpkg.com/@fullcalendar/interaction@4.3.0/main.min.js'></script> <script src='https://unpkg.com/@fullcalendar/daygrid@4.3.0/main.min.js'></script> <script src='https://unpkg.com/@fullcalendar/timegrid@4.3.0/main.min.js'></script> <script src='https://unpkg.com/@fullcalendar/list@4.3.0/main.min.js'></script> <script src='https://unpkg.com/@fullcalendar/core/locales/ja'></script>ビューファイルを作成します。
今回は/resources/views/admin/schedule/calendar.blade.phpで作成。
html部分はこんな感じです。calendar.blade.php<div class="container"> <div class="row justify-content-center"> <!-- left --> @include('admin.menu') <div class="col-md-10"> <div class="card"> <div class="card-header"><i class="fas fa-id-card"></i> カレンダー共有</div> <div class="card-body"> @if (session('status')) <div class="alert alert-success" role="alert"> {{ session('status') }} </div> @endif <div id='external-events'> <p><strong>先生</strong></p> @foreach ( $data as $d ) <div class="fc-event">{{ $d->teacher_name }}<span style="opacity:0;">:{{ $d->id }}</span></div> @endforeach <p style="display:none;"> <input type='checkbox' id='drop-remove' /> <label for='drop-remove'>remove after drop</label> </p> </div> <div id='calendar-container'> <div id='calendar'></div> </div> </div><!-- end card-body --> </div><!-- end card --> </div> </div> </div>
fc-event
div要素(カレンダー左に列挙されてるドラッグ&ドロップ可能要素)には今回先生の名前が入るので、Controllerでフェッチしたデータをまわして動的に配置してます。同div内直後の<span style="opacity:0;">:{{ $d->id }}</span>
はeventReceive時に先生IDをイベントに登録するための苦肉の策です;不可視化してコロンの後に入れといて取り出してます;var arr = info.event.title.split(':'); info.event.setExtendedProp('teacher_id', arr[1]);続いて同ファイル内のjs部分です。fullcalendarのインスタンス作成とプロパティやらコールバックを定義します。各コールバックにビジネスロジックを記述した関数を置いていく感じになると思います。eventRenderでクリック間隔にてシングルクリックとダブルクリックを判定してるのがなんかあれですが…
events:{url}
はデータソースの取得元です。通常だとDBから取得したデータをJSONにしてエコーする処理になるでしょう。ちなみにメソッドはGET
限定です。calendar.blade.phpdocument.addEventListener('DOMContentLoaded', function() { var Calendar = FullCalendar.Calendar; var Draggable = FullCalendarInteraction.Draggable; var containerEl = document.getElementById('external-events'); var calendarEl = document.getElementById('calendar'); var checkbox = document.getElementById('drop-remove'); // initialize the external events new Draggable(containerEl, { itemSelector: '.fc-event', eventData: function(eventEl) { return { title: eventEl.innerText }; } }); // initialize the calendar var calendar = new Calendar(calendarEl, { plugins: [ 'interaction', 'dayGrid', 'timeGrid','list' ], header: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' }, allDaySlot: false, forceEventDuration : true, eventColor: 'lavender', defaultTimedEventDuration: '01:00', defaultView: 'timeGridWeek', slotDuration: '00:10:00', minTime : '10:00', maxTime : '22:10', locale : 'jaLocale', editable: true, selectable: true, allDaySlot: false, droppable: true, // this allows things to be dropped onto the calendar buttonText: { today:'今日', month:'月', week: '週', day: '日', list: 'リスト' }, events:'/admin/events/source', select: function (info) { // カレンダーセルクリック、範囲指定された時のコールバック console.log('select'); }, eventReceive: function(info) { // イベントがexternal-eventからドロップされた時のコールバック console.log('eventReceive'); }, eventDrop: function(info) { // イベントがドロップされた時のコールバック console.log('eventDrop'); }, eventResize: function(info) { // イベントがリサイズ(引っ張ったり縮めたり)された時のコールバック console.log('eventResize'); }, eventRender: function (info) { //wired listener to handle click counts instead of event type info.el.addEventListener('click', function() { clickCnt++; if (clickCnt === 1) { oneClickTimer = setTimeout(function() { clickCnt = 0; // SINGLE CLICK console.log('single click'); }, 400); } else if (clickCnt === 2) { clearTimeout(oneClickTimer); clickCnt = 0; // DOUBLE CLICK console.log('double click'); } }); } }) calendar.render(); });cf. events:に指定したURLのサンプル
/admin/events/source.php// テストとしてイベントを3つ作成してみる $data = []; $ev1 = ['id'=>'1','teacher_id'=>'18','title'=>'event1','start'=>'2020-03-17T10:00:00','color'=>'lightpink']; $ev2 = ['id'=>'2','teacher_id'=>'20','title'=>'event2','start'=>'2020-03-18T10:30:00','color'=>'lightgreen']; $ev3 = ['id'=>'3','teacher_id'=>'35','title'=>'event3','start'=>'2020-03-18T10:50:00','color'=>'yellow']; array_push($data,$ev1,$ev2,$ev3); echo json_encode($data);
teacher_id
はfullcalendarのイベントモデルではデフォルトのプロパティではないですが、未定義のプロパティはextendedProps
の下に生やしてくれます。複数系なのでタイポ注意です。値の更新はsetExtendedProp
です。// infoはコールバック時に渡されるオブジェクト // アクセス info.event.extendedProps.teacher_id // 値変更 info.event.setExtendedProp('teacher_id',40);5.ビジネスロジックの実装
ここまでくればあとはゴリゴリとビジネスロジックを書いていくだけです。
非同期にてDB更新してイベントの見た目を変更するような処理がメインになるでしょう。calendar.blade.php// eventReceive時に走る処理のサンプル // 略 eventReceive: function(info) { Create(info); }, // 略 const Create = (info) => { var dt = new Date(); info.event.setExtendedProp('identifier',dt.getTime()); // csrf。Laravelお約束 $.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } }); $.ajax({ type: 'post', data: { 'identifier': info.event.extendedProps.identifier, 'teacher_id': info.event.extendedProps.teacher_id, 'start': info.event.start, 'end': info.event.end }, datatype: 'json', url: '/admin/update' /* identifierをキーに登録or更新 */ }) .done(function(data){ json = JSON.parse(data); if ( json['result'] == 'success' ) { // サーバサイドにて設定された背景色に変更 info.event.setProp('color',json['color']); } }) .fail(function(data){ alert('error'); }); }sample(/admin/update)use App\Event; // Eventモデル使用 $event = Event::firstOrNew(['identifier'=>$request->identifier]); $event->teacher_id = $request->teacher_id; $event->start = date('Y-m-d H:i',strtotime(strstr($request->start,'GMT',true))); $event->end = date('Y-m-d H:i',strtotime(strstr($request->end,'GMT',true))); DB::transaction(function() use ($event) { try { $event->save(); } catch (\Exception $e) { // エラー処理 } }); echo json_encode(array('result'=>'success','color'=>'yellow'));生徒側のカレンダーも基本adminと一緒でよいのですが認証経路が違うので処理内容が一緒でも
events:{url}
の部分はメソッドをコピペして移植するなどの変更が必要です。それとイベントのドロップ&ドラッグのUIは必要ないので、該当箇所を削除します(html部分のexternal-events要素も忘れずに)。calendar.blade.phpdocument.addEventListener('DOMContentLoaded', function() { // 略 /* ここから var Draggable = FullCalendarInteraction.Draggable; var containerEl = document.getElementById('external-events'); var calendarEl = document.getElementById('calendar'); var checkbox = document.getElementById('drop-remove'); // initialize the external events new Draggable(containerEl, { itemSelector: '.fc-event', eventData: function(eventEl) { return { title: eventEl.innerText }; } }); ここまで削除 */ // 略 events:'/user/events/source', /* /admin/events/sourceではNG */6.補足
イベントのクリアと再取得
週送りや月を送るとfullcalendarはイベントデータを都度イベントソースからフェッチしてくるのでイベントをクリアしてやらないと重複して表示される現象にみまわれました。以下はその対応です。キレイじゃない方法ですが載せておきます;calendar.blade.php// gCalendarはcalendarの参照 $(document).on('click','.fc-next-button,.fc-prev-button,.fc-dayGridMonth-button,.fc-timeGridWeek-button,.fc-timeGridDay-button,.fc-listWeek-button', function () { events = gCalendar.getEvents(); var len = events.length; for (var i = 0; i < len; i++) { events[i].remove(); } eventSources = gCalendar.getEventSources(); var len = eventSources.length; for (var i = 0; i < len; i++) { eventSources[i].remove(); } gCalendar.addEventSource('/admin/events/source'); gCalendar.refetchEvents(); });イベントIDについて
イベントモデルではデフォルトでid
が用意されていますが、クライアント処理でsetPropによる値の変更はできないようです。スタックオーバーフローではバグではないのかという議論がされていましたが、おそらく仕様だと思われます。
fullcalendarがイベント作成時に自動で採番してくれるわけではないのでextnededPropsでしのぎました。calendar.blade.phpvar dt = new Date(); info.event.setExtendedProp('identifier',dt.getTime());最後に
ここで紹介したのはほんの一部です。
いろいろできるので公式のぞいてみてください。
- 投稿日:2020-03-21T18:01:21+09:00
Fuelを使ってパラメータ付きPOSTリクエストを送信した
なんとかHTTPリクエストをAndroidのエミュレーターからローカルホストのLaravelサーバーに送信することができたので投稿します。
ライブラリのインストール
app/build.gradle
にインストールするライブラリを書いていきます。app/build.gradledependencies { // Fuel for HTTP Connections implementation 'com.github.kittinunf.fuel:fuel:2.2.0' implementation 'com.github.kittinunf.fuel:fuel-gson:2.2.0' implementation "com.squareup.moshi:moshi:1.5.0" implementation "com.squareup.moshi:moshi-kotlin:1.5.0" }HTTPリクエストを送信するための
Fuel
と,JSONのパラメータを作成するためのmoshi
をインストールします。
(僕は最初,app/build.gradle
じゃなくてルートのgradleにimplementation
を書いちゃって動きませんでした...。)パラメータのフォーマットを決める
JSONのパラメータのフォーマットを決めるためのクラス
SampleRequestFormat
クラスを作成します。SampleRequestFormat.ktclass SampleRequestFormat(val kekey: String)
kekey
という名前のキーを定義しました。Androidの画面作成
画面にボタンが一つ配置されているだけのものです。
activity_debug.xml<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:id="@+id/post_debug_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button" /> </LinearLayout>リクエスト送信用のアクティビティを作成
POSTでLaravelのサーバーへリクエストをパラメータ付きで送るためのプログラムを作成します。
DebugActivity.ktimport android.os.Bundle import android.widget.Button import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.example.live_barcode_reader.request_format.SampleRequestFormat import com.github.kittinunf.fuel.Fuel import com.squareup.moshi.KotlinJsonAdapterFactory import com.squareup.moshi.Moshi class DebugActivity : AppCompatActivity() { private var button: Button? = null private val URL: String = "http://10.0.2.2:8000" private val domain: String = "/sample9999" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_debug) val targetURL: String = URL + domain val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() val requestAdaper = moshi.adapter(SampleRequestFormat::class.java) button = findViewById(R.id.post_debug_button) button?.setOnClickListener { val header: HashMap<String, String> = hashMapOf("Content-Type" to "application/json") val sampleRequest = SampleRequestFormat( kekey = "from Android" ) Fuel.post(targetURL) .header(header) .body(requestAdaper.toJson(sampleRequest)) .response { _, response, result -> println(response) println(result) } //Toast.makeText(this, "PUSHED", Toast.LENGTH_SHORT).show() } } }このとき,
URL
の中身がhttp://10.0.2.2:8000
であることを覚えておいてください!
最後に,HTTP通信の設定についてAndroidManifest.xml
に記述します。HTTP通信の設定
network_security_config.xml
というファイルを作成し,HTTP通信できるドメインを指定して許可します。network_security_config.xml<?xml version="1.0" encoding="utf-8"?> <network-security-config xmlns:android="http://schemas.android.com/apk/res/android"> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">10.0.2.2</domain> </domain-config> </network-security-config>その後,
AndroidManifest.xml
にも変更を加えます。AndroidManifest.xml<application ... android:networkSecurityConfig="@xml/network_security_config">次はLaravelのサーバーです。
サーバーサイド
web.phpRoute::post('/sample9999', 'TestController@sample9999');
sample9999
というPOSTリクエストに反応してTestController
の中のsample9999()
という関数が反応するようにします。9999という数字は少し理由があって付けました。TestController.php<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class TestController extends Controller { public function sample9999(Request $request) { $json_string = file_get_contents('php://input'); logger($json_string); $json_object = json_decode($json_string); logger(var_export($json_object, true)); logger(gettype($json_object)); logger($json_object->kekey); return 'got request'; } }とりあえずこのコードで動作確認だけしてみました。
動作確認
Androidとサーバーを起動して,エミュレータのボタンを押すと,
laravel.log[2020-03-21 08:59:49] local.DEBUG: {"kekey":"from Android"} [2020-03-21 08:59:49] local.DEBUG: (object) array( 'kekey' => 'from Android', ) [2020-03-21 08:59:49] local.DEBUG: object [2020-03-21 08:59:49] local.DEBUG: from AndroidAndroidのログ↓
I/System.out: <-- 200 http://10.0.2.2:8000/sample9999 Response : OK Length : -1 Body : got request Headers : (11) Cache-Control : no-cache, private Connection : close Set-Cookie : laravel_session=eyJpdiI6IkVrR0RxK241U0xkWkRUbFhWWmhMSXc9PSIsInZhbHVlIjoicEF2d2h5dXVDVFdmVElDc09hLzNvMFJmNmM2bitBbTRrSFBBQjVucEN1elY5eGNUQy9ZZUFqVkExdkplTGEwWSIsIm1hYyI6IjAzMTBiZWEwN2FlYmYxODhlZWQ3OWY2MmU1YTBhMWQ2MWNiYzIzMGI3ZWY1NWM0NjlhYTNkMzkxMzJiZTgyYzIifQ%3D%3D; expires=Sat, 21-Mar-2020 10:59:49 GMT; Max-Age=7200; path=/; httponly; samesite=lax Date : Sat, 21 Mar 2020 08:59:49 GMT, Sat, 21 Mar 2020 08:59:49 GMT X-Android-Selected-Protocol : http/1.1 X-Powered-By : PHP/7.3.11 Content-Type : text/html; charset=UTF-8 Host : 10.0.2.2:8000 X-Android-Received-Millis : 1584781188564 X-Android-Response-Source : NETWORK 200 X-Android-Sent-Millis : 1584781188475 [Success: [B@615227d]HTTPリクエストが送られています!
- 投稿日:2020-03-21T15:53:24+09:00
配列で受け取ったRequestに対してのバリデーション
<table> <form action="/person/create" method="post"> {{ csrf_field() }} <tr> <th>name: </th> <td><input type="text" name="person[name]" value="{{ old('name') }}"></td> </tr> <tr> <th>email: </th> <td><input type="text" name="person[email]" value="{{ old('email') }}"></td> </tr> <tr> <th>age: </th> <td><input type="text" name="person[age]" value="{{ old('age') }}"></td> </tr> <tr> <th></th> <td> <input type="submit" value="send"> </td> </tr> </form> </table>上記のように配列にまとめて
name="person[name]
のような形でフォームを送信する場合バリデーションの書き方が少し変わる
・配列名.name
・配列名.email
上記のような指定方法となる// バリデーション // # requestが配列で送られてくる場合は'配列.要素名'となる public static $rules = array( 'person.name' => 'required', 'person.email' => 'email', 'person.age' => 'integer|min:0|max:150' );
- 投稿日:2020-03-21T15:51:26+09:00
CircleCi2.0でphpunit実行時に Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate... が出たときの対処法
やってもダメだったこと
php.inimemory_limit = -1 // メモリの制限を無くす.circleci/config.yml- image: circleci/mysql:5.7 command: [--max_allowed_packet=32M] // mysqlのパケット制限を増やす解決策
.circleci/config.yml- run: command: vendor/bin/phpunit -d memory_limit=512Mphpunitの実行オプションに-d memory_limit=512Mをつけることで解決しました。
なかなか解決策が見つからなくて、かなりハマってしまいました(汗)
この記事が誰かのお役に立てたら何よりです。参考にさせていただいた記事
https://qiita.com/pinekta/items/4ece0c88402610f874d0
↓こちらに解決策が載ってました。
https://discuss.circleci.com/t/php-memory-size/7602
- 投稿日:2020-03-21T09:10:24+09:00
Laravelのサーバレス用ライブラリbrefを使い、lambdaでhello world
プロジェクトを作ってから少し手を加えるだけで、Laravelのサーバレス化ができました。
AWS上のデプロイはServerless Frameworkが全てやってくれます。composer create-project --prefer-dist laravel/laravel laravel-demo #プロジェクト作成 cd laravel-demo composer require bref/bref #肝のbrefインストール以下の編集を加えます。b508b15
.env- SESSION_DRIVER=file + SESSION_DRIVER=array + VIEW_COMPILED_PATH=/tmp/storage/framework/viewsconfig/logging.php'stack' => [ 'driver' => 'stack', - 'channels' => ['single'], + 'channels' => ['stderr'], 'ignore_exceptions' => false, ],/app/Providers/AppServiceProvider.phppublic function boot() { - // + if (!is_dir(config('view.compiled'))) { + mkdir(config('view.compiled'), 0755, true); + } } }最後に
serverless.yml
を追加します。007fb31serverless.ymlservice: laravel-demo provider: name: aws region: ap-northeast-1 runtime: provided plugins: - ./vendor/bref/bref package: exclude: - node_modules/** - public/storage - storage/** - tests/** functions: website: handler: public/index.php timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds) layers: - ${bref:layer.php-73-fpm} events: - http: 'ANY /' - http: 'ANY /{proxy+}' # artisan: # handler: artisan # timeout: 120 # in seconds # layers: # - ${bref:layer.php-73} # PHP # - ${bref:layer.console} # The "console" layer本家の
artisan
コマンド用関数ですが、私はローカルでしか実行しないのでコメントアウトしています。デプロイコマンドは
php artisan config:clear
とsls deploy
のセットです。
以下のアウトプットになりました。$ php artisan config:clear Configuration cache cleared! $ sls deploy Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: Creating Stack... Serverless: Checking Stack create progress... ........ Serverless: Stack create finished... Serverless: Uploading CloudFormation file to S3... Serverless: Uploading artifacts... Serverless: Uploading service laravel-demo.zip file to S3 (14.19 MB)... Serverless: Validating template... Serverless: Updating Stack... Serverless: Checking Stack update progress... ................................. Serverless: Stack update finished... Service Information service: laravel-demo stage: dev region: ap-northeast-1 stack: laravel-demo-dev resources: 12 api keys: None endpoints: ANY - https://td3rzowchc.execute-api.ap-northeast-1.amazonaws.com/dev ANY - https://td3rzowchc.execute-api.ap-northeast-1.amazonaws.com/dev/{proxy+} functions: website: laravel-demo-dev-website layers: None Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.さっそく https://td3rzowchc.execute-api.ap-northeast-1.amazonaws.com/dev にアクセスしてみましょう。
以上です。以下の課題に対しても記事投稿予定です。
- URLにあるステージのパスが外せないことによる不具合が多いので、カスタムドメインを導入する
- public配下が参照されず、jsとcssが参照できない。
- slsコマンドと同期して.envを環境毎に使い分けたい。
- セッション含めDB接続したいがサーバレスはコネクションプール問題がある。
- 投稿日:2020-03-21T01:38:34+09:00
Laravelでスコープ
定義方法
scope名は必ず最初にscopeを名前の初めに付ける必要がある.
・scopeUserName
・scopeBookSerialNo
上記のようにスコープ名をつける。public function scope+名前($query, 引数) { # 処理内容 return 絞り込んだビルダ(検索条件) }ローカルスコープ
定義したメソッドを呼び出す場合に実行することができるスコープ。
逆に定義していれば自動的にスコープメソッドが実行されるものはグローバルスコープ。Person.php# scopeの定義 public function scopeNameEqual($query, $str) { return $query->where('name', $str); }PersonController.phppublic function search(Request $request){ $input_val = $request->input; $item = Person::NameEqual($input_val)->first(); $params = ['input' => $input_val, 'id' => $request->id, 'item' => $item]; return view('person.find', $params); }スコープを複数繋げて利用する
2種類のスコープを定義
Model.php// $min(変数の値)以上のageを検索するスコープ public function scopeAgeGreaterThan($query, $min) { return $query->where('age', '>=', $min); } // $max(変数の値)以下のageを検索するスコープ public function scopeAgeLessThan($query, $max) { return $query->where('age', '<=', $max); }下記サンプルのように条件をつなげて使う
例:ageが "リクエストで受け取った値" 〜 "値+10" の間のデータを取得するスコープ
()Controller.php$min = $request->input * 1; $max = $min + 10; $items = Person::ageGreaterThan($min)->ageLessThan($max)->get();グローバルスコープ
明示的にメソッドを呼び出さなくても自動的にスコープの検索ありきで検索が実行される
1.use Illuminate\Database\Eloquent\Builder; ←Builder使えるようにする
2.bootメソッドでモデルの初期化を行う
3.bootメソッド内でaddGlobalScopeクロージャを書く
4.addGlobalScopeクロージャの中でスコープの処理を定義していくこうすることでモデルクラスでbootメソッドがオーバーライドされる
Model.phpuse Illuminate\Database\Eloquent\Builder; class Person extends Model { // グローバルスコープ定義 // protected static function boot() { parent::boot(); static::addGlobalScope('age', function(Builder $builder){ $builder->where('age', '>', 20); }); } ... ... ... }Scopeクラス作成(グローバルスコープ)
複数のモデルや、その他のプロジェクトなどでも利用されるような汎用性の高い処理は
Scopeクラス
を使うと便利.Scopeクラスの作成
1.Scopesディレクトリ(名前は任意)を作成。
2.Scopeクラスの作成(仮でScopePersonというクラスにします)常に20歳より年上のみを取得するスコープを作成するため下記コード記載
Scopes/ScopePerson.php<?php namespace App\Scope; use Illuminate\Database\Eloquent\Scope; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; class ScopePerson implements Scope { public function apply(Builder $builder, Model $model) { $builder->where('age', '>', 20); } }Model側でグローバルスコープを定義
・useしてScopePersonを参照できるようにしたらstatic::addGlobalScope(New ScopePerson)
してあげるだけModel.phpuse App\Scope\ScopePerson; // グローバルスコープ定義 // protected static function boot() { parent::boot(); static::addGlobalScope(New ScopePerson); }こうすることで
Person::all();
等で複数データを取得した場合に自動的にageが20以下のものはフィルターされる
- 投稿日:2020-03-21T00:42:20+09:00
VSCユーザーがLaravelのデータベース接続設定でエラーが出た時、確認すべきこと
こんにちは!今回がエンジニア初学者がついうっかり詰まってしまった初歩的なエラーとその原因をご紹介していきたいと思います。
LaravelのDB接続設定エラー
入力した項目は合っているはずなのに、なぜかDB接続できない。
そんな時は、もう一度書き込んでいるファイルが正しいか確認してみてください!
もしかしたら.env
ファイルではなく.env.example
に設定を書き込んでいるかもしれません。
DB接続設定を書き込むのは.env
ファイルです!なぜこのエラーにハマったのか
原因は、普段使用しているVisualStudioCode(以下VSC)のクイックオープン(Cmd + P)でファイル検索をした時に、
.env
が引っ掛からず、ついうっかり検索結果に出ていた.env.example
を私が.env
だと誤認したからでした。対応策
なぜ
.env
ファイルが検索結果に出なかったのか。
それはVSCの設定で.ignoreファイルと.gitignoreファイルを検索対象から外すようにしていたからです。
検索対象に入れたい場合は、VSCの環境設定に入って検索ボックスからSearch:Use Ignore Files
を検索し、チェックを外しておくようにしましょう。
- 投稿日:2020-03-21T00:34:38+09:00
初学者がLaravelの独学に関してまとめてみた(教本/学習サイトもまとめてます)
初学者がLaravelの独学に関してまとめてみた(教本/学習サイトも)
Laravelの独学に関して
私はLaravelの学習を1月ごろから始めました。プログラミングの学習自体は11月より始めた素人に毛がレベルの輩です。日々学習中です。
ですが、このタイミングでなぜまとめたかと言いますと。。。
①沢山学習していると自分でもアウトプットしないと考えがまとまらない。今後の学習計画が経てずらい。
②少なからず、簡易的なポートフォリオは作ることができたので、これから学習する方の参考(ロードマップ)に少しでもなれば嬉しい。以上の理由からです。
間違いなどもあるかもしれません。温かく見守っていただけますと幸いです。(また、ご教示いただけますと幸いです。)
具体的なレベル感としては以下のポートフォリオを参考にしていただければと思います。
https://chobimusic.com/laravel_craftbeers/
※ログイン機能含め、基本的な機能は実装できましたが、投稿したユーザーだけが削除&編集できる権限の実装(認可周り)などはできておりません。
※これから認可やポリシーなどの理解を深め、且つVue.jsとの連携までしたポートフォリオ作成に挑戦しようと思っている段階です。
活用した教本のまとめ
Laravelの学習において参考にした教本を購入した順番でまとめていきます。※私はスクール等には通わず、ほぼ1人で本とネットを頼りに学習しています。
(※お金がないだけ。。。泣)まず最初に手に取ったのはこの本。独学で勉強していたら誰もが目にすると思います以下の本。
①PHPフレームワーク Laravel入門 第2版
https://www.amazon.co.jp/PHP%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF-Laravel%E5%85%A5%E9%96%80-%E7%AC%AC2%E7%89%88-%E6%8E%8C%E7%94%B0%E6%B4%A5%E8%80%B6%E4%B9%83-ebook/dp/B08625YD7H/ref=pd_aw_sbs_351_2/357-1916791-6282364?_encoding=UTF8&pd_rd_i=B08625YD7H&pd_rd_r=02491f21-b407-4270-b76b-e3574f21718c&pd_rd_w=97xhS&pd_rd_wg=0donR&pf_rd_p=1893a417-ba87-4709-ab4f-0dece788c310&pf_rd_r=CRQSKHHSFS9DS01SWGQ6&psc=1&refRID=CRQSKHHSFS9DS01SWGQ6こちらでまずは勉強しました。(逆に他に入門的なLaravel本がない気がします。)
とても参考になります。Laravelの基礎を体系的に学ぶことができます。これを読めば、それなりに入門系チュートリアルは挑戦できると思います。
※以下、バカみたいにメモったこの本の見開き1ページ目※
ただし、個人的には、ミドルウェアを割と序盤で解説されて、全く理解できかった過去があるので、順番入れ替えてユーザー認証、アクセス権限に活用する実践ベースで紹介してほしかったなと思います。
また、Webアプリ(SNSぽい感じで)を作りたいとすれば、認可など深いところまでは学べまないので足りないです。別途、学ぶ必要があります。
また、この本だけだとモチベーションが担保が難しく、また実際にどうアプリ制作に活かしていくのかも掴みにくかったので、Techpitさんの教材やネットに転がっているいろんなチュートリアルを漁っては、アプリ制作も並行してしていました。
個人的には、本で体系的に知識を身に付けることも大切ですが、本を読むより、実際に模写しながらアプリ制作やってた時のほうが知識が定着していく感じがありました。(学習サイトに関しては後述しております。効率と自分に合った学習法を見つけて勉強していきましょう。)
そして、この教本といくつかのチュートリアルでアプリ制作を終えたころに、ポートフォリオ作成に取り組みました。(正直、取り掛かるのは早ければ早いほどいいと思います。)
完成したものが既述のポートフォリオになります。ログイン機能含め、基本的な機能は実装しましたが、投稿したユーザーだけが削除&編集できる権限の実装などはできませんでした。(最近やっと理解し始めたので、徐々に修正していきます。)
正直、ここの部分までわかりやすく解説してくれる教材を当時見つけられなかった。(実際とても少ない気がします。めちゃくちゃ探しました。)
そして、さらに調べまくってようやくミドルウェア・認可・ポリシー辺りの重要性に気付き始めます。
さらなるここら辺のスキルアップを試みて、次はこの本を次に手に取ります。
②PHPフレームワークLaravel Webアプリケーション開発
https://www.amazon.co.jp/PHP%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AFLaravel-Web%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E9%96%8B%E7%99%BA-%E7%AB%B9%E6%BE%A4%E6%9C%89%E8%B2%B4-ebook/dp/B07SPT6XJV/ref=sr_1_4?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=Laravel&qid=1584693544&s=digital-text&sr=1-4注意点としてはLaravelのバージョンが5.5で記載されていることです。認証周りなどで多少バージョン6と異なる部分があります。
手に取ったときは、よくチュートリアルで紹介されているVagrant、Laradockに関しても学べるので一石二鳥だと思っていました。
ところがどっこい。
第1章の環境構築でいきなり躓く。。。笑笑
それ以外にも総じて難しく、投げ出してしまいました。(おい)
この経緯があって、今現在までXAMPPで頑張っている感じです。
その後、一時はモチベーションがかなり落ちてしまって、他言語に浮気とかもしていました。
ですが、結局既述のポートフォリオ以上のものを作れるイメージは湧きませんでした。
結局、どの言語を使ったとしても、ミドルウェアなどの知識を身に付けないと更なる向上はないと気づきました。一言語で理解を深めようと決めました。
(どの言語もベースとなる考え方は同じ!!)で、この本より少し簡単そうだなという理由でずっとレビューでは賛否両論だったので避けていたこの本にも手を出します。
③PHPフレームワーク Laravel実践開発
https://www.amazon.co.jp/PHP%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF-Laravel%E5%AE%9F%E8%B7%B5%E9%96%8B%E7%99%BA-%E6%8E%8C%E7%94%B0%E6%B4%A5%E8%80%B6%E4%B9%83-ebook/dp/B07WW45Q1X/ref=pd_aw_sbs_351_1/357-1916791-6282364?_encoding=UTF8&pd_rd_i=B07WW45Q1X&pd_rd_r=02491f21-b407-4270-b76b-e3574f21718c&pd_rd_w=97xhS&pd_rd_wg=0donR&pf_rd_p=1893a417-ba87-4709-ab4f-0dece788c310&pf_rd_r=CRQSKHHSFS9DS01SWGQ6&psc=1&refRID=CRQSKHHSFS9DS01SWGQ6ですが、結局まだあまり読めていません。というより改めてこの本をパラパラめくっていて思ってしまったんです。
さっさと次のポートフォリオを作ったほうが良い!!ってことに。笑 ←now!!です。
ちなみにVue.jsなどフロント言語フレームワークとの連携の記述もありますが、かなり導入部分についてしか触れられていません。
ですが、Techpitの教材などで理解できなかった際の参考に横に置いておくと安心感があります。
(たまに参考にします。)Laravel+Vue.jsのポートフォリオ作成へ挑戦したいとのことでしたら、以下のTechpit教材などで実際に作りながら学ぶことをおすすめします。(ちなみに②の本でもフロント言語フレームワークとの連携に関しては触れられていません。Laravel特化。)
https://chobimusic.com/laravel_vue_sns/
また、Vue.jsの基本的な知識もマストで必要になります。以下の猫本が有名&おすすめです。(私も学習中です。一緒に頑張りましょう。)
https://chobimusic.com/vue_nekotodo_arrange/
参考にさせていただいた学習サイトまとめ
いろいろな本を手に取って学習してきましたが、やはり1から100まで完璧に身に付けながら読むのは骨が折れます。というよりも、いきなり初学者が全て理解しようとするのは無理だと思います。モチベ担保&知識の関連付けにも、前述の本などと並行して以下のような学習サイトで実際にアプリ作成することをおすすめします。
・Techpit学び放題プラン(月額2980円)
https://techpit.thebase.in/items/24273300多種多様なアプリを作りながら学習することができます。このサイトと出会えたことで独学でここまでこれたと言っても過言ではありません。※一部単発購入が必要になるものもアリ。
・入門Laravelチュートリアル(無料)
https://www.hypertextcandy.com/laravel-tutorial-introductionToDoアプリ制作。終盤ポリシーやエラー画面の実装までします。ここまで扱う無料コンテンツはこれだけ??ありがたすぎます。ちなみにLaravel+Vue.jsのチュートリアルもあります。こちらはVueRouterなどVue.jsに関する理解をしっかりしていないと非常に難しいため、私もまだ手が届いておりません。。。泣
・【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第1回DB設計とMigration)
https://qiita.com/namizatop/items/d79017aa474966244073
SNS系無料チュートリアル。個人的にはとても参考になりました。(作者もNBA好きぽいのでシンパシーを感じます。)
おわりに
以上です。改めて自身のアウトプットの拙さにも嫌気がさしましたが、少しでもどなたかの参考になりましたら幸いです。肝心の今後に関してですが、まだ読破できていない教本2冊とTechpitの教材を参考にしながらVue.jsまで連携させたポートフォリオ制作に挑戦しようと考えています。
※現在3/21(日)このタイミングでTechpitでLaravel+Vueに関して学べる教材(前述)がリリースされたのは本当にラッキーです。このような教材を待望してました。皆さんもチェックしてみてください◎
また、Techpit教材でのMailhog実装失敗などの経験から、XAMPP環境のみならず、Laradock(もしくはVagrant)での環境構築もできたほうが良いなと思っています。こちらに関しては、改めて②の本とネットの力をお借りして、引き続き挑戦していきたいと思っております。(※Dockerの本も探しましたが、Laradockに関しては扱っている本も少なくググったほうが参考資料が多いなと感じました。)
最後まで読んでいただきありがとうございました。
おしまい。
- 投稿日:2020-03-21T00:33:58+09:00
Laravel ベースレイアウトを使って表示するページのheaderやfooterのデザインを統一しよう
目的
- ベースレイアウトの定義と継承を行いデザインの統一化やコーディングの簡略化の方法を学んだので忘れない様にまとめる
- 主に下記の本に記載されていたことを筆者なりに解釈してまとめる(筆者の文章が稚拙なため、深く勉強したい方は書籍を購入した方が良いかも知れない)
ベースレイアウトとは?
- 文字通りビューファイルのベースとなるレイアウトである。
- アプリケーション内で共通のデザインや共通のhtmlのhead情報などを予め記載しておくことができる。
- ベースレイアウトをビューファイルで継承すれば簡単にデザインの統一ができる。
わかった気になれる考え方(認識違ったらすいません)
ベースレイアウトはスーパーで売っているお弁当を入れる容器をイメージしていただきたい。
ちょっとお堅い考え方(認識違ったらすいません)
- ベースレイアウトでは表示するページの大枠の部分を定義する。
- 共通するhtmlのhead情報、headerの場所、mainの場所、footerの場所などなど
- header、main、footerなどの場所はそれぞれセクションと呼ばれる単位で定義する。
- headerセクション、mainセクション、footerセクションを定義した場合、そのベースレイアウト継承した別のビューファイルで各セクションに入れるコードを指定することができる。(詳しい方法は後述する。)
- また先に記載したセクションだけでなく任意の名前のセクションを作成することができる。
headerとfooterのデザインを共通にするための書き方の例
- ※フォルダ名、ファイル名は時に決まりはないため、例と完全一致してなくても良いが、お試しで実施する場合下記の名前でベースレイアウトを記載するファイルとそれを格納するフォルダを作成することをお勧めする。
- laravelのviewファイル内にlayoutというフォルダを作成する。
- layoutフォルダ内にapplication.blade.phpというファイルを作成する。
- application.blade.phpファイルがベースレイアウトを記載するファイルである。
application.blade.phpに下記の記載を行う。
application.blade.php<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <header> <h1>これはheaderです</h1> </header> <main> <div> @yield('content') </div> <footer> <h1>これはfooterです</h1> </footer> </main> </body> </html>お好きな名前で新規にビューファイルを作り、そのビューをブラウザで表示できる最低限の処理をルーティングとコントローラに記載する。
新規のビューファイルに下記の記載を行う。
@extends('layout.application') @section('content') <p>これはベースレイアウトで作成したコンテンツです。</p> @endsection新規のビューファイルを表示リンクをブラウザで開き、表示を確認する。
簡単な解説
@yield('セクション名')
- ここに後述するセクション宣言時に記載したコードが入ってブラウザで表示される。
- セッションを呼び出している。
- お弁当の容器でいうところの具材が入る区画である。
@extends('ベースレイアウト格納フォルダ名.ベースレイアウトファイル名')
- これは継承の設定である。
- ビューファイルに記載する。ベースレイアウトのファイルには記載しない。
@section('セクション名')
@endsection
- セクションの宣言である。
- セクション内に記載したいコードを
@section('セクション名')
から@endsection
の間に記載する。- お弁当の容器でいうところの具材の決定である。
上記をまとめると
- セクションの宣言は
@section('セクション名')
と@endsection
の間にセクションに記載したいコードを書く。@yield('セクション名')
の部分に@section('セクション名')
と@endsection
の間に書かれたコードが記載されて表示される。- ベースレイアウトとページ表示用のビューファイルは
@extends('ベースレイアウト格納フォルダ名.ベースレイアウトファイル名')
を用いて継承を行うことで初めてセッション宣言とセッションの呼び出しは使用できない。- 今回はheaderとfooterのデザイン統一を目的としておりベースレイアウトにセッションの呼び出しを記載し、ビューファイルにセッションの宣言を記載したが、目的が異なる場合ベースレイアウトでのセッション宣言、ビューファイルでのセッションの呼び出しを行うことももちろんあり得る。
すいません
- 筆者なりに考えてわかりやすく伝えようとした結果、とてもわかりにくい説明になってしまったことをお詫びします。
- 記事内のリンクはアフィリエイトリンクではないのでご安心ください。
- 投稿日:2020-03-21T00:21:01+09:00
【Laravel】Herokuでファイルアップロードのサイズ上限値を変更する(Apache)
事象確認
post_max_sizeはデフォルトで8Mが設定されているようです。
$ heroku logs -t 2020-03-20T14:58:54.361490+00:00 app[web.1]: [20-Mar-2020 14:58:54 UTC] PHP Warning: POST Content-Length of 9423684 bytes exceeds the limit of 8388608 bytes in Unknown on line 0手順
公式ドキュメントによると、
.user.ini
ファイルを作成してあげれば良いようです。Customizing Web Server and Runtime Settings for PHP
https://devcenter.heroku.com/articles/custom-php-settings#php-runtime-settingspublicディレクトリに
.user.ini
ファイルを作成し、POSTの最大サイズとファイルの最大サイズを設定します。
git pushして変わっていることを確認しましょう。public/.user.inipost_max_size = 20M upload_max_filesize = 5M