- 投稿日:2020-09-22T22:28:48+09:00
【ローカル環境】Laravelアプリ作ってローカルDB(MAMP)にテーブル作るまで
Laravelでいろいろなハンズオン形式の練習をしていくにあたり、
アプリ作成からテーブル作成まで、毎回ちょっと調べるのが面倒になってきたのでまとめます。前提環境/どんな方向け?
・mac OS 10.15(10.14や10.13でも大きな違いはなし)
・MAMPをmacにインストール済みであること。
・ターミナル操作がわからないこともないこと。
・Laravelはmacにインストール済みであること。
・composerも入れていること。という感じで、しょっぱなからの説明ではなく、過去にlaravelをmampで構築されており、ローカル環境で新しくアプリを作る方向け(主に自分の備忘録)です。
手順
1.Laravelアプリを作る
まずはアプリ作成。「todoLife」というアプリを作る場合は下記の手順。
ターミナルで、composer create-project "laravel/laravel=5.8.*" todoLife
※バージョン指定は任意です。不要な場合は「laravel/laravel」でokです。
ここでは5.8の中の最新版を指定してみます。また、このコマンドを行うことで、「今いる場所」にフォルダ/プロジェクトを作ります。
例えばデスクトップに「todoLife」というフォルダを作り、その中にcdで移動して上記のコマンドを実行すると
desuktop/todoLife/todoLife (← laravelのアプリ)
という感じになっちゃいます。
ので、desktopに作りたい場合はデスクトップに移動して実行しましょう。
(この後すぐmamp内に移動しますが)2.MAMP内に移動
作ったララベルプロジェクトをmampの中のhtdocsに移動しましょう。
具体的には下記のような配置となります。Macintosh ハードディスク/Applications/MAMP/htdocs/プロジェクタフォルダ
phpデータをローカルのmampで表示、動作させるにはこの「htdocs」の中に保管している必要があります。
3.DBを作る
MAMPで予めDBを作っておきます。
開発環境ではあるので大体でもいいかもですが、アプリ名と同じにしているケースが多いです。MAMPの中でnewから作成しましょう。
※照合順序は「utf8_general_ci」でokです。
4.テーブルを作る
手動でMAMP内で作ってもいいですが、ここではlarabelのマイグレーション機能で作ってみます。今回作るのはシンプルに「todo」のテーブル「todos」です。
※なお、laravelのプロジェクト > database > migrations の中にデフォルトで存在している
2014_10_12_000000_create_users_table.php
2014_10_12_100000_create_password_resets_table.phpは、ユーザーテーブルやパスワードリセットが不要ならば削除しても問題ないです。
マイグレーションとは?
マイグレーションを行うことにより、このmigrationsファイルの中の「up()」の処理を実施します。
この後マイグレーションファイルの中身を見ますが、これらのファイルのupでは、テーブルを作るという処理が書かれています。
そのため、デフォルトの状態でマイグレーションをすると、前述の既存の二つのファイルにより、「ユーザーテーブル」と、「パスワードリセットのテーブル」が作られる、ということになります。今回はそのままにしておき、マイグレーションファイルを別途作ります。
作る方法は、ターミナルでプロジェクトフォルダのトップにいるときにphp artisan make:migration create_todos_table --create=todos
と実施します。
ここで中盤と最後にある「todos」が、作成するファイル名、テーブル名になります。
テーブル名は通常複数形として作成する必要があります。postなら「posts」などです。次にmigrationsの中にできた記述を見てみましょう。こんな関数ができているはずです。
2020_xx_xx_123630_create_todos_table.phpclass CreateTodosTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('todos', function (Blueprint $table) { $table->bigIncrements('id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('todos'); } }up()が、migration実行時に作られるテーブルの情報、そして
down()がやっぱその処理をやめるときに巻き戻す(ロールバック時に)実行される処理です。今回、各todosには題名と本文をつけたいので、こんな感じでtitle、bodyテーブルを作る記述を追加します。
public function up() { Schema::create('todos', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('title',50);//追加 $table->text('body');//追加 $table->timestamps(); }); }それでは、下記の手順でマイグレートを実行します。
php artisan migrate
5.環境設定
DBとの接続設定をまだ行っていませんでした。
その辺の環境設定は.envファイルで行います。
.envはプロジェクトのトップディレクトリ直下に存在しています。
いじるのはこの辺。DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=todoLife //アプリ名に変更 DB_USERNAME=root DB_PASSWORD=root //パスワードを設定。mamp側でDBにログインするためのユーザーごとのid、パスワードを設定します。config/app.php
config/database.php
というファイルも編集します!//app.php //タイムゾーンと言語設定を変更しておきましょう。 'timezone' => 'Asia/Tokyo', 'locale' => 'ja',//database.php //database名、ユーザーネーム、パスワード、unix_socketを修正します。 'mysql' => [ 'driver' => 'mysql', 'url' => env('DATABASE_URL'), 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'todoLife'),//修正 'username' => env('DB_USERNAME', 'root'),//修正 'password' => env('DB_PASSWORD', 'root'),//修正 'unix_socket' => env('DB_UNIX_SOCKET', '/Applications/MAMP/tmp/mysql/mysql.sock'),//修正6.もう一度マイグレーション
mampを見てもちゃんとtodos、users、passwordresetテーブルがつくられています。
終わり!
おまけ
マイグレーションしてなくても見れますが、プロジェクトフォルダでphp artisan serveして、
mampを起動しておけばlaravelの起動も確認できます!
- 投稿日:2020-09-22T20:20:28+09:00
適当な文字列を 0 から 1 までの実数に振り分けるロジック (PHP/TypeScript)
最近 A/B テストのように確率的な条件分岐を実現する文脈で、適当な入力文字列から区間 $ [0, 1] $ に含まれる数値を出力するようなハッシュ関数を実装する機会がありました。単に定められた範囲の数値を出力するだけでなく、出力される数値に偏りが出ないように(関数の出力値の分布が連続一様分布とみなせる 1 ように)するという要件もありました。
イメージとしては以下のコードのようになります。
// 同じ文字列を入力に取ると同じ数値が出力される strToNumberBetweenZeroAndOne('abc'); // output: 0.7283949105904 strToNumberBetweenZeroAndOne('abc'); // output: 0.7283949105904 // 別の文字列を入力に取ると別の数値が出力される strToNumberBetweenZeroAndOne('Hello'); // output: 0.095208030923864 strToNumberBetweenZeroAndOne('World!'); // output: 0.31755707966684 strToNumberBetweenZeroAndOne('0123456789'); // output: 0.51892998626938 // 色々な文字列に対して出力値をプロットしていくと、数値の確率分布が区間 [0, 1] の一様分布に近づく。このような関数を PHP と TypeScript で実装したので紹介します。
実装
PHP
PHP の場合は以下のように実装できます。
function strToNumberBetweenZeroAndOne(string $message): float { // 1 $hashHex = hash('sha256', $message); // 2 $maxHex = str_repeat("f", 64); return hexdec($hashHex) / hexdec($maxHex); }1 の部分では
hash('sha256', $string)
で入力文字列を SHA256 2 で64桁の固定長16進数に変換し、hexdec()
でその10進数表現を得ています。文字列から16進数への変換は bin2hex で行うこともできますが、文字列のサイズが大きすぎると桁あふれを起こしてしまうので使用を避けています。2 の部分では 1 で得られた10進数を64桁の16進数の最大値
str_repeat("f", 64)
の10進数表現で割ることで数値を算出しています。TypeScript
import CryptoJS from 'crypto-js' const strToNumberBetweenZeroAndOne = (message: string): number => { const hashHex = CryptoJS.SHA256(message).toString() const maxHex = 'f'.repeat(64) return parseInt(hashHex, 16) / parseInt(maxHex, 16) }内容は PHP と特に変わりませんが、 SHA256 のハッシュ化を行うために crypto-js というライブラリを利用しています。
検証
実際にランダムな文字列を入力にメソッドの出力値 3 を集計してヒストグラムを作成してみると、おおよそ一様分布になっていることが分かります。
- 投稿日:2020-09-22T19:40:32+09:00
Docker x Laravel phpMyAdmin のコンテナを構築する
以前の記事(最強のLaravel開発環境をDockerを使って構築する【新編集版】)のコメントにてphpMyAdminを使用したいという要望がありましたので補足の記事を作成させていただきます。
手順
https://github.com/ucan-lab/docker-laravel
docker-compose.yml に phpMyAdmin のサービスを追加
docker-compose.ymlvolumes: # 追記 pma-session-store: services: # 追記 pma: image: phpmyadmin/phpmyadmin:5.0.2 environment: - PMA_HOST=db - PMA_USER=root - PMA_PASSWORD=secret ports: - 8080:80 volumes: - pma-session-store:/sessionsDockerコンテナの構築
$ docker-compose up -dphpMyAdmin へアクセス
上記のURLへアクセスし、phpMyAdminの画面が表示されればokです。
補足
MySQL新認証プラグイン caching_sha2_password
私のdbコンテナの設定でMySQL8.0系で認証方式を新しいcaching_sha2_passwordに設定しています。phpMyAdminは5.0.1以降でないとcaching_sha2_passwordに対応していないのでタグで新しいバージョンを明示的に指定してます。latestが5系を指すようになったら外してもいいかもです。
蛇足
(MySQLのCLIやSequel Ace等のクライアントツールで特に不便がないのでがあるので個人的にはphpMyAdminは入れなくてもいいかなと思ってます)
- 投稿日:2020-09-22T18:31:38+09:00
PHPでconfigファイルをオートロードで呼び出す方法
Laravelをやっているとconfigから値を呼ぶことがあります。
同様に素のPHPでも使いたいなと思ったのでやり方を書いておきますConfig.php<?php class Config { protected static $directory; public static function setConfigDirectory($directory) { self::$directory = $directory; } public static function getConfigDirectory() { return rtrim(self::$directory, '/\\'); } public static function get($s) { $values = preg_split('/\./', $s, -1, PREG_SPLIT_NO_EMPTY); $key = array_pop($values); $file = 'common.php'; $path = (!empty($values)) ? implode(DIRECTORY_SEPARATOR, $values) . DIRECTORY_SEPARATOR : ''; $base_dir = self::getConfigDirectory() . DIRECTORY_SEPARATOR; $config = include($base_dir . $path . $file); return $config[$key]; } }これの他に
config/common.php
を作ってくださいcommon.php<?php return [ 'STRIPEKEY' => 'abc', 'ZOOMKEY' => '123', ];読み込みたいファイルで
example.php<?php require_once 'Config.php'; Config::setConfigDirectory(__DIR__ . '/config');これを追記してください
蛇足ですが、
require
,include
の違いはファイルが読み込めなかった場合 require文の場合はFatalエラーで処理が終了するが include文の場合はWarnigエラーで処理が継続されることですrequire_once, include_onceの違いは
同じファイルを読み込んだ場合に _once文は一度しかファイルを読み込まないが require, include文は何度でも読み込むことです
つまりほとんどの場合はrequire_onceでいいですね
gitにpushするときは
.gitignoreconfig/ Config.php #任意を追記すればいいでしょう
example.php<?php require_once 'Config.php'; Config::setConfigDirectory(__DIR__ . '/config'); echo Config::get('STRIPEKEY'); //abc echo Config::get('ZOOMKEY'); //123読み込むのがめんどくさいときはオートロードしましょう
オートロードはクラスを利用する際に自動的にそのクラスが定義されているファイルを読み込む機能です
処理上のオーバーヘッドを防ぐために使うといいですね!
spl_autoload_register()
関数を使う
オートロードの処理をコールバック関数として任意のタイミングで登録できて
__autoload()
関数と違って複数のオートロード関数を登録しておくこともできるlib/autoload.php<?php spl_autoload_register( function($class_name) { $filepath = __DIR__ . '/' . $class_name . '.php'; if (is_readable($filepath)) require $filepath; if ($class_name == 'Config') Config::setConfigDirectory(__DIR__ . '/config'); } ); #複数作ると必要なクラスの定義が見つかるまで登録した順にコールバック関数が実行されるそれを読み込もう
example.php<?php require 'lib/autoload.php'; echo Config::get('ZOOMKEY'); //123これでおっけい!
- 投稿日:2020-09-22T17:16:26+09:00
入力欄が別の時の期間カスタムバリデーションLaravel5.5
入力欄が別の期間のバリデーションをかけようと思うと
2015-3~2017-4
ではバリデーションがうまくかかるが、
2018-8~2018-6
だとエラーを吐かなかったので
バリデーションをかける前に連結させました。
誰かの参考になればと思い、残しておきますlaravel5.5で
FormRequest
を使う場合コントローラの処理を実行する前にバリデーションがかかってしまうので、
先に文字列を連結させる必要がありました。
なので文字列を連結させてそれをフォームから送信させているようにしてバリデーションをかけるようにしました。もちろん入力欄を分けなければ
after
やafter_or_equal
、before
、before_or_equal
などを使えばいいのですが。。
送信前にリクエストを加工するには6.xではprepareForValidation()
メソッドを使えばいいみたいなので参考にはならないと思いますまずはこれを追加してください
use Illuminate\Validation\Validator;その後にメインロジックを追加していきます
見ての通り、連結させて$request
に追加しています
それに伴ってprotected function validationData() { $this->addStartOfRegisterDate(); $this->addEndOfRegisterDate(); $this->addStartOfEnrollmentDate(); $this->addEndOfEnrollmentDate(); return parent::validationData(); } private function addStartOfRegisterDate() { $start_of_register_year = (string)$this->request->get('start_period_of_register_year'); if ($start_of_register_year === '') return; $start_of_register_month = (string)$this->request->get('start_period_of_register_month'); if ($start_of_register_month === '') return; $start_of_register_date = $start_of_register_year . '-' . $start_of_register_month; $this->request->add(['start_of_register_date' => $start_of_register_date]); } private function addEndOfRegisterDate() { $end_of_register_year = (string)$this->request->get('end_period_of_register_year'); if ($end_of_register_year == '') return; $end_of_register_month = (string)$this->request->get('end_period_of_register_month'); if ($end_of_register_month == '') return; $end_of_register_date = $end_of_register_year . '-' . $end_of_register_month; $this->request->add(['end_of_register_date' => $end_of_register_date]); } private function addStartOfEnrollmentDate() { $start_of_enrollment_year = (string)$this->request->get('start_period_of_enrollment_year'); if ($start_of_enrollment_year === '') return; $start_of_enrollment_month = (string)$this->request->get('start_period_of_enrollment_month'); if ($start_of_enrollment_month === '') return; $start_of_enrollment_date = $start_of_enrollment_year . '-' . $start_of_enrollment_month; $this->request->add(['start_of_enrollment_date' => $start_of_enrollment_date]); } private function addEndOfEnrollmentDate() { $end_of_enrollment_year = (string)$this->request->get('end_period_of_enrollment_year'); if ($end_of_enrollment_year == '') return; $end_of_enrollment_month = (string)$this->request->get('end_period_of_enrollment_month'); if ($end_of_enrollment_month == '') return; $end_of_enrollment_date = $end_of_enrollment_year . '-' . $end_of_enrollment_month; $this->request->add(['end_of_enrollment_date' => $end_of_enrollment_date]); }これによって連結された文字列が$requestに入るので、そこにバリデーションをかけるようにしましょう
'start_of_register_date' => 'nullable|before_or_equal:end_of_register_date', 'end_of_register_date' => 'nullable|after_or_equal:start_of_register_date', 'start_of_enrollment_date' => 'nullable|before_or_equal:end_of_enrollment_date', 'end_of_enrollment_date' => 'nullable|after_or_equal:start_of_enrollment_date',これを
rules()
のreturn
に追加しましょう全体を見るとこんな感じですー
MypageUpdateRequest.php<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Validator; class MypageUpdateRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'birth_year' => 'nullable|integer|min:' . config('common.validate_min.birth_year') . '|max:' . config('common.validate_max.birth_year'), 'birth_month' => 'nullable|integer|min:' . config('common.validate_min.birth_month') . '|max:' . config('common.validate_max.birth_month'), 'birth_day' => 'nullable|integer|min:' . config('common.validate_min.birth_day') . '|max:' . config('common.validate_max.birth_day'), 'start_period_of_register_year' => 'nullable|string|required_with:company_name,start_period_of_register_month,end_period_of_register_month|integer|min:1969|max:date(Y)', 'start_period_of_register_month' => 'nullable|string|required_with:company_name,start_period_of_register_year,end_period_of_register_year|integer|min:1|max:12', 'start_of_register_date' => 'nullable|before_or_equal:end_of_register_date', 'end_period_of_register_year' => 'nullable|string|required_with:company_name,start_period_of_register_month,end_period_of_register_month|integer|min:1969|max:date(Y)', 'end_period_of_register_month' => 'nullable|string|required_with:company_name,end_period_of_register_year,start_period_of_register_year|integer|min:1|max:12', 'end_of_register_date' => 'nullable|after_or_equal:start_of_register_date', 'start_period_of_enrollment_year' => 'nullable|string|required_with:school_name,start_period_of_enrollment_month,end_period_of_enrollment_month|integer|min:1969|max:date(Y)', 'start_period_of_enrollment_month' => 'nullable|string|required_with:school_name,start_period_of_enrollment_year,end_period_of_enrollment_year|integer|min:1|max:12', 'start_of_enrollment_date' => 'nullable|before_or_equal:end_of_enrollment_date', 'end_period_of_enrollment_year' => 'nullable|string|required_with:school_name,start_period_of_enrollment_month,end_period_of_enrollment_month|integer|min:1969|max:date(Y)', 'end_period_of_enrollment_month' => 'nullable|string|required_with:school_name,start_period_of_enrollment_year,end_period_of_enrollment_year|integer|min:1|max:12', 'end_of_enrollment_date' => 'nullable|after_or_equal:start_of_enrollment_date', ]; } protected function validationData() { $this->addStartOfRegisterDate(); $this->addEndOfRegisterDate(); $this->addStartOfEnrollmentDate(); $this->addEndOfEnrollmentDate(); return parent::validationData(); } private function addStartOfRegisterDate() { $start_of_register_year = (string)$this->request->get('start_period_of_register_year'); if ($start_of_register_year === '') return; $start_of_register_month = (string)$this->request->get('start_period_of_register_month'); if ($start_of_register_month === '') return; $start_of_register_date = $start_of_register_year . '-' . $start_of_register_month; $this->request->add(['start_of_register_date' => $start_of_register_date]); } private function addEndOfRegisterDate() { $end_of_register_year = (string)$this->request->get('end_period_of_register_year'); if ($end_of_register_year == '') return; $end_of_register_month = (string)$this->request->get('end_period_of_register_month'); if ($end_of_register_month == '') return; $end_of_register_date = $end_of_register_year . '-' . $end_of_register_month; $this->request->add(['end_of_register_date' => $end_of_register_date]); } private function addStartOfEnrollmentDate() { $start_of_enrollment_year = (string)$this->request->get('start_period_of_enrollment_year'); if ($start_of_enrollment_year === '') return; $start_of_enrollment_month = (string)$this->request->get('start_period_of_enrollment_month'); if ($start_of_enrollment_month === '') return; $start_of_enrollment_date = $start_of_enrollment_year . '-' . $start_of_enrollment_month; $this->request->add(['start_of_enrollment_date' => $start_of_enrollment_date]); } private function addEndOfEnrollmentDate() { $end_of_enrollment_year = (string)$this->request->get('end_period_of_enrollment_year'); if ($end_of_enrollment_year == '') return; $end_of_enrollment_month = (string)$this->request->get('end_period_of_enrollment_month'); if ($end_of_enrollment_month == '') return; $end_of_enrollment_date = $end_of_enrollment_year . '-' . $end_of_enrollment_month; $this->request->add(['end_of_enrollment_date' => $end_of_enrollment_date]); } public function messages() { return [ 'birth_year.integer' => '正しい年を選択してください', 'birth_year.max' => '正しい年を選択してください', 'birth_year.min' => '正しい年を選択してください', 'birth_year.required_with' => '月と日も選択してください', 'birth_month.integer' => '正しい月を選択してください', 'birth_month.max' => '正しい月を選択してください', 'birth_month.min' => '正しい月を選択してください', 'birth_month.required_with' => '年と日も選択してください', 'birth_day.integer' => '正しい日を選択してください', 'birth_day.max' => '正しい日を選択してください', 'birth_day.min' => '正しい日を選択してください', 'birth_day.required_with' => '月と日も選択してください', 'start_period_of_register_year.integer' => '在籍期間を正しく選択してください', 'start_period_of_register_year.max' => '在籍期間を正しく選択してください', 'start_period_of_register_year.min' => '在籍期間を正しく選択してください', 'start_period_of_register_year.required_with' => '年も選択してください', 'start_period_of_register_month.integer' => '在籍期間を正しく選択してください', 'start_period_of_register_month.max' => '在籍期間を正しく選択してください', 'start_period_of_register_month.min' => '在籍期間を正しく選択してください', 'start_period_of_register_month.required_with' => '月も選択してください', 'start_of_register_date.before_or_equal' => '在籍開始期間を正しく選択してください', 'end_period_of_register_year.integer' => '在籍期間を正しく選択してください', 'end_period_of_register_year.max' => '在籍期間を正しく選択してください', 'end_period_of_register_year.min' => '在籍期間を正しく選択してください', 'end_period_of_register_year.required_with' => '年も選択してください', 'end_period_of_register_month.integer' => '在籍期間を正しく選択してください', 'end_period_of_register_month.max' => '在籍期間を正しく選択してください', 'end_period_of_register_month.min' => '在籍期間を正しく選択してください', 'end_period_of_register_month.required_with' => '月も選択してください', 'end_of_register_date.after_or_equal' => '在籍終了期間を正しく選択してください', 'start_period_of_enrollment_year.integer' => '在籍期間を正しく選択してください', 'start_period_of_enrollment_year.max' => '在籍期間を正しく選択してください', 'start_period_of_enrollment_year.min' => '在籍期間を正しく選択してください', 'start_period_of_enrollment_year.required_with' => '年も選択してください', 'start_period_of_enrollment_month.integer' => '在籍期間を正しく選択してください', 'start_period_of_enrollment_month.max' => '在籍期間を正しく選択してください', 'start_period_of_enrollment_month.min' => '在籍期間を正しく選択してください', 'start_period_of_enrollment_month.required_with' => '月も選択してください', 'start_of_enrollment_date.before_or_equal' => '在籍開始期間を正しく選択してください', 'end_period_of_enrollment_year.integer' => '在籍期間を正しく選択してください', 'end_period_of_enrollment_year.max' => '在籍期間を正しく選択してください', 'end_period_of_enrollment_year.min' => '在籍期間を正しく選択してください', 'end_period_of_enrollment_year.required_with' => '年も選択してください', 'end_period_of_enrollment_month.integer' => '在籍期間を正しく選択してください', 'end_period_of_enrollment_month.max' => '在籍期間を正しく選択してください', 'end_period_of_enrollment_month.min' => '在籍期間を正しく選択してください', 'end_period_of_enrollment_month.required_with' => '月も選択してください', 'end_of_enrollment_date.after_or_equal' => '在籍終了期間を正しく選択してください', ]; } }
- 投稿日:2020-09-22T17:01:07+09:00
phpで切り捨て処理を行う際、floor関数とintキャスト処理のどちらを使うのが適切か
やりたいこと
浮動小数点数の切り捨て処理を行い、float 型を int 型にしたい。
floor()
関数- intのキャスト処理
のどちらを利用するのが適切か。それとも、どちらでも差異はないのか。
結論
floor()
関数は返り値として float型を返すため、int型でキャストする必要がある。- マイナス値を扱う場合において、
floor()
と(int)
の結果が異なってくる。そのため、
- -1.1を切り捨てした際、-1になるのが自然と感じるのであれば、
(int)$num
とする- -1.1を切り捨てした際、-2になるのが自然と感じるのであれば、
(int)floor($num)
とするという選択肢の、どちらかを選ぶのがよいと考えられる。
マイナス値を与えたときの処理の違いについて
floor()
関数の場合value をこえない最大の整数の値を返します。 floor() の返り値は float 型のままとなります。
https://www.php.net/manual/ja/function.floor.phpマイナス値 (例:
-1.1
) を与えると、-2
を返す<?php $float_vals = array(1.1, 1.8, -1.1, -1.8, 0.05, -0.05); foreach ($float_vals as $v) { var_dump(floor($v)); }float(1) float(1) float(-2) * float(-2) * float(0) float(-1) *
(int)
キャストの場合float から整数に変換する場合、その数はゼロのほうに丸められます。
https://www.php.net/manual/ja/language.types.integer.phpマイナス値 (例:
-1.1
) を与えると、-1
を返す<?php $float_vals = array(1.1, 1.8, -1.1, -1.8, 0.05, -0.05); foreach ($float_vals as $v) { var_dump((int)$v); }int(1) int(1) int(-1) * int(-1) * int(0) int(0) *マイナス値の切り上げ・切り捨て処理には、4種類あるみたい
- 0への丸め
- 無限大への丸め
- 負の無限大への丸め
- 正の無限大への丸め
wikipedia - 端数処理
※ wikipediaまでしか確認していないため、本当にこのような用語が定義されているかについてはわかりません。
参考程度にお願いします。floor() 関数はなぜ値をfloat型で返すのか
表すことのできる数値の範囲を大きく取るためです。
以下記事のコメントにて、説明がありました。
https://qiita.com/takepan/items/ff8bba234fdc93a32791#comment-50d3e6e3db22ffdd53dafloorは返り値として float を返すため、取り得る値の範囲が広くなります。
floatであれば格納可能な値 9223372036854775808 をintにキャストすると、オーバーフローを引き起こします。実際にコメントのサンプルコードを実際に実行すると、以下のようになります。
<?php $large_number = PHP_INT_MAX + 100.1; var_dump(sprintf('%.30f', floor($large_number))); var_dump(sprintf('%.30f', intval(floor($large_number)))); ?>string(50) "9223372036854775808.000000000000000000000000000000" string(51) "-9223372036854775808.000000000000000000000000000000"オーバーフローしました。
おわりに
思いの外深い話題でした。
もっと掘り下げられそうなのですが、下書きが10件溜まってしまったため一度記事にします1
この記事を書くのに疲れて、結局今日書こうと思っていた記事は書けそうにない。。。あとおなかすいた ↩
- 投稿日:2020-09-22T15:52:28+09:00
PHPにおける文字列置き換え大全
コピペして使うように作りましたー
間違ってたら教えてください!!目次
よく使う正規表現のパターンマッチ
・携帯番号の例
・郵便番号の例
・全角カタカナの例
・なんちゃってメールアドレスの例
・クレジットカード全社の例
・IPアドレスの例
文字列を繰り返す方法
入力値を統一する方法
・小文字から大文字に変換
・大文字から小文字に変換
・全角を半角に変換
・半角を全角に変換
・カナ文字の変換
文字エンコードの変換
文字列の一部を切り抜く方法
文字列の一部が一致しているか確認する
文字列の置き換えよく使う正規表現のパターンマッチ
正規表現の基本的な構文
構文 説明 . 改行文字以外の任意の一文字 ? 0回か1回だけのマッチ * 0回以上の繰り返し + 1回以上の繰り返し {n} n回の繰り返し {n,m} n回以上m回以上の繰り返し ^ 文字列及び行の始まり $ 文字列及び行の終わり [] 文字クラス[a-z]ならaからzまでの一文字 \A 文字列の始まり \z 文字列の終わり \w 単語構成文字[a-zA-Z0-9_]とは異なる可能性がある \d 数字 \s スペースタブ改行などの空白文字 \S 空白以外の全ての文字 () パターンのグループ化
パターン修飾子はデリミタ(\)の後に書く
パターン修飾子 説明 i 大文字と小文字を区別しない m 行単位でマッチ s 「.」を改行文字にもマッチさせる u パターン文字列をUTF-8エンコードとして扱う x エスケープするか文字クラスの内部を除いてパターン中の空白文字を無視
\
をマッチさせるには
\\\\
とする必要がある
正規表現でもPHPでもエスケープするため携帯番号の例
example.php//080-1234-5678, 09012345678 $tel_pattern = '/\A(090|080|070)-?\d{4}-?\d{4}\z/';郵便番号の例
example.php//111-1112, 1111112 $postal_code_pattern = '/\A\d{3}-?\d{4}\z';全角カタカナの例
example.php$em_kana_pattern = '/\A[\x{30A1}-\x{30FC}().-]+\x/u';半角カタカナの例
example.php$en_kana_pattern = '/\A[\x{FF66}-\x{FF9F}]+\x/u';なんちゃってメールアドレスの例
example.php$mail_pattern = '/\A[\p{L}0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[\p{L}0-9!#$%&\'*+\/=?^_`{|}~-]+)*@(?:[_\p{L}0-9][-_\p{L}0-9]*\.)*(?:[\p{L}0-9][-\p{L}0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,})\z/ui';クレジットカード全社の例
example.php$cards_pattern = '/\A(4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|^(?:2131|1800|35\d{3})\d{11}$)\z/'; $visa_pattern = '/\A4\\d{12}(\\d{3})?\z/'; $master_pattern = '/\A5[1-5]\\d{14}\z/'; $jcb_pattern = '/\A(3\\d{4}|2100|1800)\\d{11}\z/'; $amex_pattern = '/\A3[4|7]\\d{13}\z/'; $diners_pattern = '/\A(?:3(0[0-5]|[68]\\d)\\d{11})|(?:5[1-5]\\d{14})\z/'; $discovery_pattern = '/\A(?:6011|650\\d)\\d{12}\z/';IPアドレスの例
example.php$ipv4_pattern = '\A(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\z'; $ipv6_pattern = '\A((([0-9A-Fa-f]{1,4}:){7}(([0-9A-Fa-f]{1,4})|:))|(([0-9A-Fa-f]{1,4}:){6}(:|((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})|(:[0-9A-Fa-f]{1,4})))|(([0-9A-Fa-f]{1,4}:){5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4}){0,1}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)(:[0-9A-Fa-f]{1,4}){0,4}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(:(:[0-9A-Fa-f]{1,4}){0,5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))(%.+)?\z';参考
https://qiita.com/ryounagaoka/items/dc9fe731d0fa6f8f9daa文字列を繰り返す方法
str_repeat
を使うexample.phpstr_repeat('*', 10);入力値を統一する方法
小文字から大文字に変換
strtoupper()
関数を使う
mb_strtoupper()
関数を使えば全角のアルファベットも変換する
ucfirst()
関数は最初の文字だけを大文字に変換し
ucwords()
関数は最初の単語だけを大文字に変換します大文字から小文字に変換
strtolower()
関数を使う
mb_strtolower()
関数を使えば、全角のアルファベットも変換する全角を半角に変換
mb_conver_kana()
関数を使う
オプション 説明 r 全角英字を半角に変換 n 全角数字を半角に変換 a 全角英数字を半角に変換 s 全角スペースを半角に変換 example.phpmb_convert_kana($text, 'r');半角を全角に変換
同じく
mb_convert_kana()
関数の第二引数を使う
オプション 説明 R 半角英字を全角に変換 N 半角数字を全角に変換 A 半角英数字を全角に変換 S 半角スペースを全角に変換 カナ文字の変換
同じく
mb_convert_kana()
関数の第二引数を使う
オプション 説明 k 全角カタカナを半角カタカナに変換 K 半角カタカナを全角カタカナに変換 h 全角平仮名を半角平仮名に変換 H 半角カタカナを全角平仮名に変換 c 全角カタカナを全角平仮名に変換 C 全角平仮名を全角カタカナに変換 V 濁点付きの文字を一文字に変換、KやHと共に使う s 全角スペースを半角に変換 S 半角スペースを全角に変換 example.phpmb_conver_kana($text, 'HV');文字エンコードの変換
mb_convert_encoding()
関数を使うexample.phpmb_convert_encoding(mb_convert_encoding($text, 'SJIS'), 'UTF-8', 'SJIS'); mb_convert_encoding(mb_convert_encoding($text, 'EUC-JP'), 'UTF-8', 'EUC-JP');文字列の一部を切り抜く方法
mb_substr('文字列', '開始位置'[, '文字数'])
を使う
文字数を省略した場合、開始位置から末尾までの文字列の全てを返す
また先頭は0から始まる
マイナスを指定することもできるexample.php$text = 'asdfghjkl'; echo mb_substr($text, 0, 5); //asdfg echo mb_substr($text, 2); //dfghjkl echo mb_substr($text, -5); //lkjhg文字列の一部が一致しているか確認する
mb_strpos()
関数を使う
最初に見つかった位置を数値で返すexample.phpif (mb_strpos($target, $keyword) === false) echo $keyword . 'が見つかりませんでした';演算子は'==='を使う必要があります
検索する文字が先頭に見つかってしまった場合に0を返すからです。文字列の置き換え
str_replace()
関数を使うexample.phpecho str_replace('before', 'after', $target);
- 投稿日:2020-09-22T12:54:12+09:00
Laravel8以降でルーティングがうまくいかない件
Laravel8.Xからrouteの記述方法が変わって面倒になったかもしれません。(自分でも検証済み)
Laravel7.X以前→Laravel8.X以降はエラーになったと思ったが、解決した件。route/web.php
Route::get('/', 'TestController@index');laravel8.X以降はこのようにしないとうまく行かなかった。。
route/web.phpuse App\Http\Controllers\TestController; Route::get('/',[TestController::class, 'index']);しかし、Laravel8でも、app/Providers/RouteServiceProvider.php の bootメソッドでnamespaceを指定すればこれまで通りの動作をすることがわかった。
(土日にも関わらず、疑問に対応いただいた私の上司に感謝。。)app/Providers/RouteServiceProvider.php
public function boot() { $this->configureRateLimiting(); $this->routes(function () { Route::middleware('web') ->namespace('App\Http\Controllers') ← 【これを足す】 ->group(base_path('routes/web.php')); Route::prefix('api') ->middleware('api') ->group(base_path('routes/api.php')); }); }これで今まで通り(Laravel7以前のようにルーティングがうまくいく)
routes/web.phpRoute::get('/test', 'TestController@index');以上
- 投稿日:2020-09-22T11:17:23+09:00
Laravelでapi.phpに送れない
何をしているか
https://www.hypertextcandy.com/vue-laravel-tutorial-authentication-part-4/
上記サイトを参考にLaravel × Vue のアプリ開発の勉強をしていますただ、Laravelの更新により、現在のバージョンではコピペでエラーが出てしまうことがありました。
躓いた箇所
上記サイトのところで、api.phpにgetを渡したのにも関わらず、web.phpの
Route::get('/{any?}', fn() => view('index'))->where('any', '.+');
が参照されてしまいました。解決策
app\Providers\RouteServiceProvider.php の箇所で
public function boot() { $this->configureRateLimiting(); $this->routes(function () { Route::middleware('web') ->group(base_path('routes/web.php')); Route::prefix('api') ->middleware('web') ->group(base_path('routes/api.php')); }); }となっている箇所を
public function boot() { $this->configureRateLimiting(); $this->routes(function () { Route::prefix('api') ->middleware('web') ->group(base_path('routes/api.php')); Route::middleware('web') ->group(base_path('routes/web.php')); }); }に変更しました。
要するにwebとapi の読み込む順番を逆にするということです。同じところで躓いた人は試してみてください。
- 投稿日:2020-09-22T10:17:10+09:00
Laravel_CRUD処理を使った投稿アプリを作成する その10 バリデーションエラーを日本語に変える
目的
- アプリを作成する上で基本となるCRUD処理を有したLaravelアプリをチュートリアル的に作成する方法をまとめる
実施環境
- ハードウェア環境
項目 情報 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
- ソフトウェア環境
項目 情報 備考 PHP バージョン 7.4.3 Homwbrewを用いて導入 Laravel バージョン 7.0.8 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする 前提条件
- 実施環境に近い環境が構築されていること。
- 筆者は下記の方法で環境構築を行った。
前提情報
- ソースコードはこちら→https://github.com/miriwo0104/laravel_crud/tree/master
- DockerやAWSなどは使用せずにMacのローカルに実施環境と同じLaravel開発環境を構築して実施する。
- チュートリアルで実際に筆者が作成したソースコードをGitHubにて公開予定である。
- CRUD処理の作成完了を最短目標にしてバリデーションなどは後々設定することとする。
- 実施環境と同じ環境がDockerやAWSで用意できるなら都度読み替えていただければ実施が可能だと思う。
- 公式ドキュメントと一冊の技術書を元に本記事を記載する。
- Laravel 7.x
- PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応
- 本記事はシリーズとして内容を分割する予定である。記事のタグ「miriwo_laravelチュートリアル」を本シリーズの記事に付与するのでそのほかの記事がみたい方は活用していただきたい。
- DBへの意図しないデータの流入を防ぐためバリデーションを実装する。
- 問い合わせの内容は「入力がされていること」のバリデーションルールを、新規投稿と投稿編集は「入力されており、かつ140文字以内であること」定義してバリデートする。
- バリデーションで弾かれた時に出力されるエラーメッセージを自分好みの物に変える方法を記載する
- バリデーションルールは下記の公式ドキュメントに数多く記載されているので実装できたら遊んでみるのもいいかもしれない。
この記事の読後感
- バリデーションエラーをユーザ任意の物に変更することができる。
概要
- Requestファイルの修正
- 確認
詳細
Requestファイルの修正
laravel_crudディレクトリで下記コマンドを実行してリクエストファイルを開く。
$ vi app/Http/Requests/ContentRequest.php開いたコントローラファイルを下記のように修正する。
laravel_crud/app/Http/Requests/ContentRequest.php<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class ContentRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'content' => ['required', 'max: 140'], ]; } // 下記を追記する public function messages() { return [ 'content.required' => '内容を記入してください。', 'content.max' => '投稿内容は140文字以下にしてください。', ]; } // 上記までを追記する }laravel_crudディレクトリで下記コマンドを実行してリクエストファイルを開く。
$ vi app/Http/Requests/InquiryRequest.php開いたコントローラファイルを下記のように修正する。
laravel_crud/app/Http/Requests/InquiryRequest.php<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class InquiryRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'content' => ['required'], 'name' => ['required'], ]; } // 下記を追記する public function messages() { return [ 'content.required' => '内容を記入してください。', 'name.required' => '内容を記入してください。', ]; } // 上記までを追記する }確認
laravel_crudディレクトリで下記コマンドを実行しローカルサーバを起動する。
$ php artisan serve下記にユーザ認証後、下記にアクセスする。
何も入力せず「送信」をクリックし下記のようにエラーが出ることを確認する。
下記にユーザ認証後、下記にアクセスする。(コンテンツのidは1以外でも構わない)
何も入力せず「送信」をクリックし下記のようにエラーが出ることを確認する。
下記にユーザ認証後、下記にアクセスする。(コンテンツのidは1以外でも構わない)
何も入力せず「送信」をクリックし下記のようにエラーが出ることを確認する。
参考文献