gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

経過観察

1/27
超備忘録。

いち。
水槽の縁に微妙に「消えない泡」。汚れが処理仕切れてないみたい。まぁある程度もくろみ通りなので、生体のいない(水草だけの)水槽に、今日もきょうとて、少量の「アカヒレ用のエサ」を投入。
明日くらいまでかなぁ。水曜日は何も入れず、木曜日の水質の数値が異様なモノでなければ、予定通り今週末にパイロットフィッシュ・アカヒレさんを召喚しませう。
数値がアウトな感じだったら、(水替えする、ではなくて)もうしばらく、水草だけで回しておく予定。

に。
AT-20で、どうも「少し高い目の位置から水を落とす」ほうがエアレーション的効果が望めそうなので。
超手っ取り早く「外掛けフィルターの下に台を置いて少しだけ持ち上げる」作戦。うちの20cmキューブの水槽で、95mm~100mmくらいの高さがよいぽい。
百均で100mm程度の高さの木の箱を購入。心持ち、5mm程度高めではあるけど、いい感じ。

さん。
やっぱり外掛けフィルター AT-20のお話。
バイオビーズをふよつかせる空間が微妙に狭い……中間のフィルタが膨らんで、前面にはカスタムイン入れてるんだけど、後部の「バイオビーズ予定空間」が狭すぎる……ので、割と躊躇なく、あっさりと改造。
やったのは
・正規のバイオパックを外す
プラ板で仕切りを作る
くらい。

バイオパックの活性炭、もったいないから使おう……かと思ったんだけど案外にでかいので水槽に微妙に入れにくく、速攻で「お疲れ様でした」。
プラ板、厚さ0.3mmとかで「えっれぇペラッペラ」で不安をもちながら。とりあえずバイオパックの、白いフィルタ外した骨格に大体添うようにカットしてみる。……一発で入る。
底に半分だけ切れ目をいれて水が通りやすいように加工。
多分「プラ板だけ」だと上手く止まらないんだけど、カスタムイン入れると丁度よい押さえになるので、いい感じ。
ただ、ちょいと不安なんで、プラ板をもう1枚切り出して重ねる……この手の薄いプラスチック、ぬれてると大変によく張り付くので、楽(笑
高さを軽く調節して終了。所要時間5分程度(笑

これで、明日到着のバイオビーズが楽しみってぇもんである。

追記
マツモが底面から抜けた(苦笑
ある意味丁度よいので、そのままふわふわと漂わせっぱなしにしてます現在。

ハイグロフィラ ポリスペルマが、5本中、1本だけ抜けちゃいましたなぁ。再度刺そうと思ったんだけど、なんかへにゃっとなってて刺せない。
これもとりあえず漂わせておきませう……なんか元気がないので、しばらく様子見、かな。

ボトルアクアリウム

妄想、かつ、メモ。

気にしてるのが「いろんなサイクルが回しにくいなぁ」って思ったんだけど、ふと、アイデアが浮かんできてしまったので、とりあえずメモ。
後で推敲しませう。

ボトルは「パンドルチェの瓶」。直径8cm、高さ15cmの円柱、なので、多分、お水は700mlも入らないと思われる、を、前提に。

底面は適度になにか。なんとなし「吸着系のソイル」が良さそうな気はしている。
立ち上げ時に「第一水槽でしばらく泳がせたバイオビーズ(か、フィルターのバイオビーズを何割か拝借)」を入れてバクテリアを移植 & お水も第一水槽から移動。

メインは水草
ウィローモス
・マツモ
(・アナカリス)
あたりかなぁ。

で。「第一水槽のメンテナンス」の時に
・ボトルから1/3~1/2くらい水を抜く
・第一水槽の水をボトルに入れる
を追加する事で「第一水槽の汚れた(=栄養豊富な)水」を定期的に追加。

で、藻がでたりしたら、第一水槽のミナミヌマエビくんを1~2匹。水合わせも
・適当なタッパに、第一水槽からミナミヌマエビくんを移動させる
・ゆっくりと水を合わせる
・そのままボトルにぶち込む
で、(ボトルアクアリウムはヒーター入れてないから)温度も大体合わせられるから、移動させやすいだろうし。

光は「第一水槽の横」で、第一水槽の光をそのまま拝借(必要なら後でライト買えばよいし)。

これだと
・機材がそんなにいらない
水草の領域を増やせる
・ボトルアクアリウムでいくつか気になってた所が(とりあえず)解決する
ので。第一水槽が立ち上がった後、であれば、まぁまぁいけるんじゃなかろうか?

後で見直して推敲しよう。
どのみち、第一水槽がちゃんと立ち上がった以降のお話、だし。

………これはまぁ「元々あったものを使う」ので、増えるにはカウントしない、ってことで、一つ(笑

アクアリウム始めました(笑

当初は「ボトルアクアリウム」の予定だったんだけどなぁ……なおいちゃんの、主にメモ書き(笑

1/21に、アクアリウムの先輩(笑)から「ウィローモスならちょうど増えすぎて刈らないとなんで、刈ったやつ送りましょうか?」との申し出があり、ほいほいと好意に甘える(笑

一発目のお買い物は1/22。
本当は、アスウォット株式会社さん( http://aswot.com/baghis/ )の「パンドルチェ」の空き瓶でボトルアクアリウムを作ろうか、と思ったのですが。
空きスペースはかって(最大で20cm*30cm)、お買い物に行って思案している内に方針変更がありまして。

・コトブキ レグラスR-200(200×200×200mmサイズ、水容量7L)
・テトラ オート ワンタッチフィルター AT-20
・コトブキ フラットLED SS(20~32㎝水槽、全光束190lm)
・プリセットオートAR30(25.5~27.0℃ ±1.5℃:4W)
・アクシータイマー(定格入出力容量:1500Wまで)
・水作 プロホース エクストラ Sサイズ

あたりを購入。
底は「サンディブライト」の、粒の粗めなほうを購入。
あとは「スドー 水草のソフトおもり 巻き型」「流木」を、水草用に購入。カルキ抜きは「水替えプラス」、あと一応「BICOM スターターキット」を購入。

一式、アクアフォレスト新宿店さん( http://aquaforest.tokyo/ )にお世話になりました(ってかそれ以降の買い物も大分とお世話になってます)。

で、送って貰える水草
南米ウィローモス
オレゴンリバーモス
マツモ
アマゾンフロッグピット
との事。色々調べて見て、ワクテカを募らせつつ、まずは水を回し始める。スターターキットも投入……ちょっと早まったかなぁ、とか思いつつ。


翌日
1/23 に、色々調べて、生体濾過系のブツが足りないなぁ、と思って
マツダ ストレーナースポンジ ミニ 細目
・パワーハウス (AAJ9Q) カスタムイン 50 ソフトタイプ
を追加で購入。あと、アンモニアがないなぁ、と思ったので、アクアフォレストさんが「ソイル少量」ってのを売ってたのでそれも購入……そういえばどの種類なんだろう? 今度聞いてみよう。

で、水草が届いたので早速レイアウト。
レイアウトには
・絹糸(木綿糸ってあったけど100均にあったのが木綿糸だった)
・入れて貰ったビニタイ
・買ったソフトおもり
を使って。ソイルは、砂利の上にうっすらと蒔きました。ソイルがくずれたらプロホースで吸い上げればいいかなぁ、とか思ってますが、ど~なんでしょう??

あとは
・小型のあみ
・でっかいスポイト(水合わせ用)
をアクアフォレストさんで購入。

100均で
・プラスチックのパッド(作業用)
・二リットルの麦茶とか作る容器(水替え用)
・500mlの計量カップ(水を注ぐとき用)
・鏡(側面において、光を反射させたら具合がよいかなぁ、と)
を購入。
ピンセット代わりは菜箸。いや便利便利。これでいいかなぁ、って思うくらいに(笑

水槽がちょいと白濁してる……けど、水草だけだしあんまり気にしない。


その翌日
1/24 に、「もう一つくらい水草が欲しいかなぁ」と思って「簡単そうで」「根っこをはってくれる子(底面の環境がよくなると聞いた)」ってことで
・ハイグロフィラ ポリスペルマ
を選んでいただいて購入。
すげぇ刺しやすい(笑
あと、水質チェックした方がよいよ、と言われて、「テトラ 6in1」も購入。
チェックするまでもなく白濁が消えた……思ったより早いなぁ。こーゆーものらしい。


1/25
パイロットフィッシュの投入時期に悩みつつ、一瞬「………アンモニア水買うか?」とか悩んだ経由、「パイロットフィッシュに与える餌を少量入れる」に落ち着いたので。
アカヒレ飼うつもりなので、アクアフォレストさんでアカヒレに与えてるって言われて
・お魚ぱくぱく
を購入。
15粒くらい投入(明日からもうちょっと控え目予定)、ふと「今の水を一回はかろう」と思ってはかってみると「亜硝酸 1mg」「硝酸 25~50mgの間」くらい………あれ? アンモニアの数値を図りたい所ではあるんだけど、なんか思ったより落ち着いてる???

とはいえまぁそこは「こってこてが好きなおいちゃん」なので。
色々調べて悩んだ挙げ句(結構色々、ろくでもないことを考えた経由)。とりあえず「バイオビーズ」なるものが比較的よろしそうなので、購入予定。さっき、チャームさん( https://www.shopping-charm.jp )でぽちった。
ついでに
水草メンテ用はさみ
・(安かったから)ピンセット
・具合の良さそうなスポイト(GEX ジェックス メダカ元気 スポイト)
を購入。
……なんか調べると、値段も色々あったり、モノによってはヨドバシさんで扱ってたりもするから、そろそろ「落ち着いて調べてから購入」とかしてもよいかも、とか思ってみたり。

 

とりあえず今後の予定としては
・1月末か2月頭に、パイロットフィッシュとしてアカヒレを一匹、召喚
・2月半ばに、アカヒレをもう一匹、召喚
・3月に入ったら、本命のミナミヌマエビを、5~10匹、召喚
する予定でございます。

いにしえ(かもしれない)技法:bit演算変

bit演算を使ったプログラミング、割と「知られてないのでは?」という感じがあったので。
ここでは少し抽象的な部分が混ざってしまうのだけど、まぁなんかの一助になるかなぁ、くらいの感じで、めも。

大まかには
・処理が「A」「B」「A+B」みたいな感じの時の処理の共通化
・いわゆるフラグ的に on/off 出来るステータスがあるようなもの
の時に便利だったりします。

前提

まず、2進数はよいよね?
2進数は、各桁が「bit」って言い方をします。
んで、1だと「立ってる」、0だと「寝てる/立ってない/落ちてる」とかって言い方をする事が、まぁ。
1の「立ってる」は割と共通な気がするなぁ。0はあちこち方言がありそうだけど。

んで。論理演算のANDは「あるbitが、立っているか寝ているか」を判定するのに割と便利につかえます。

例。
変数 $x*1の1桁目(2^0)が立っているか寝ているかを判定する場合は

if (0 !== ($x & 0b0001)) {
    // 立ってる
} else {
    // 寝てる
}

って判定が出来ます。まぁもうちょっと省略して

if ($x & 0b0001) {
    // 立ってる
} else {
    // 寝てる
}

って書く事が多いように思いますが。

同じように、2桁目(2^1)が立っているかどうかは

if ($x & 0b0010) {

3桁目(2^2)なら

if ($x & 0b0100) {

で判定が出来ます。
とりあえずわかりやすく二進数表記で書いてますが、実際には「1、2、4」といった、十進数で書いている事もちょいちょい。

これ自体は、2進数とbit演算を知っていれば「知識としては知っている」内容だと思うのですが、これを上手く使うと、プログラムが楽に出来ます。
いやどちらかというと「いにしえから伝わる技法」だと思うのですが、割と昨今でも普通に使う事が多々あるので。

ちなみに、こーゆーのを「マスクビット」とか「ビットマスク値」とかいう言い方をする事があるので、知っておくと、ググる時に楽……かも。

処理が「A」「B」「A+B」みたいな感じの時の処理の共通化

まぁ今回の元ネタがこっちなので、先にこっちで(笑
入ってくるパラメタによって「Aのみ」「Bのみ」「AとBの処理」をやる場合。
一番ざっくりと書くと

if (パラメタ==A) {
    パラメタAの処理;
    パラメタAの処理;
    パラメタAの処理;
} else if (パラメタ==B) {
    パラメタBの処理;
    パラメタBの処理;
} else if (パラメタ==A+B) {
    パラメタAの処理;
    パラメタAの処理;
    パラメタAの処理;
    パラメタBの処理;
    パラメタBの処理;
}

となるか、と思います。

で、少し共通化すると

function func_A() {
    パラメタAの処理;
    パラメタAの処理;
    パラメタAの処理;
}
function func_B() {
    パラメタBの処理;
    パラメタBの処理;
}

//
if (パラメタ==A) {
    func_A();
} else if (パラメタ==B) {
    func_B();
} else if (パラメタ==A+B) {
    func_A();
    func_B();
}

ってなるか、と思うのですが。

これを、bit演算を使うと

$type = 0;
if (パラメタ==A) {
    $type = 1; // 0b0001
} else if (パラメタ==B) {
    $type = 2; // 0b0010
} else if (パラメタ==A+B) {
    $type = 3; // 0b0011
}
//
if ($type & 1) { // 0b0001
    パラメタAの処理;
    パラメタAの処理;
    パラメタAの処理;
}
if ($type & 2) { // 0b0010
    パラメタBの処理;
    パラメタBの処理;
}

って書く事ができます。

いやまぁ関数(メソッド)に切り出してもよいんですが。
「微妙に切り出しにくい(面倒な)処理」とかで、かつ「ここでしか使わない」ようなロジックの場合、こんな風に書くと
・重複した記述がいらない
・(慣れてれば)見やすい
ので、すっきり書けますよ、的な感じになります。

いわゆるフラグ的に on/off 出来るステータスがあるようなもの

この辺は、PHPの関数でも割とおなじみなように思うのですが。
ふと思い出しやすい所で、 file_put_contents( https://www.php.net/manual/ja/function.file-put-contents.php ) を例に。

第三引数のflagsが、まさにこのやり方をやっています。

<?php

var_dump(FILE_USE_INCLUDE_PATH);
var_dump(FILE_APPEND);
var_dump(LOCK_EX);

int(1)
int(8)
int(2)

4がいませんが、まぁ2進数にすると
0b0001
0b1000
0b0010
で、ちょうどそれぞれ「1つだけbitが立っている」値になっています。

なので、例えば「FILE_APPENDかつLOCK_EXにしたい」場合は、論理和(or)を使って「 FILE_APPEND | LOCK_EX 」とやると、$flgには10(0b1010)が渡るので。
あとは、プログラムで

if ($flg & 0b1000) {
    // FILE_APPEND用の処理
}
if ($flg & 0b0010) {
    // LOCK_EX用の処理
}

って書き方をするから便利なんですね。

……と、そういえば error_reporting( https://www.php.net/manual/ja/function.error-reporting.php )もこれをがっつり使ってましたねぇ。
こっちは https://www.php.net/manual/ja/errorfunc.constants.php で(とりあえず現在のバージョンの時の)「各定数の数値」まで書いてあるので、見てみるとよいか、と。
値を2進数にしてみるとわかるのですが、いずれも「1箇所だけ1、残りは0になってる2進数」になっているかと思います。
だから、error_reportingの値は、主にor(|)をつかって「複数指定」が出来るんですね*2

この辺、多分今でも(そして多分、今後も)なにげに出てくると思われるので、知っていると便利かなぁ、と。

*1:一応、PHPerだからねぇおいちゃんw

*2: 「bit反転(~)+and(&)による一部bit(主にNotice)寝かせ」は、邪悪な行為なのでおいちゃんのコズムではリアリティにより否定されます

PHP初心者本 覚え書き

ちょっとまぁやりたい事、があって。
まずは「PHPの初心者本」のうち、現在入手可能と思われるやつ(で、版を重ねてるものはそれの最新)を、ざっくりと列挙。

PHPの絵本 第2版 https://www.amazon.co.jp/dp/4798151645
よくわかるPHPの教科書 https://www.amazon.co.jp/dp/4839964688
Webサイト制作者のための PHP入門講座 https://www.amazon.co.jp/dp/4774145254
いきなりはじめるPHP https://www.amazon.co.jp/dp/4897978858
気づけばプロ並みPHP 改訂版 https://www.amazon.co.jp/dp/4865940650
最初のキホンがやさしくわかる 絵で学ぶPHP入門 https://www.amazon.co.jp/dp/4048868691
改訂新版 ゼロからわかるPHP超入門 https://www.amazon.co.jp/dp/4774178918
詳細! PHP 7+MySQL 入門ノート https://www.amazon.co.jp/dp/4800711304
スラスラわかるPHP https://www.amazon.co.jp/dp/4798125806
独習PHP 第3版 https://www.amazon.co.jp/dp/479813547X
PHP7+MariaDBMySQLマスターブック https://www.amazon.co.jp/dp/4839962340
いちばんやさしい PHP の教本 第2版 https://www.amazon.co.jp/dp/4295001244
スラスラ読める PHPふりがなプログラミング https://www.amazon.co.jp/dp/4295007676
誰もがあきらめずにすむPHP超入門 https://www.amazon.co.jp/dp/4797398973
確かな力が身につくPHP「超」入門 https://www.amazon.co.jp/dp/4797388722
30時間アカデミックPHP入門 https://www.amazon.co.jp/dp/4407340355
イラストでよくわかるPHP はじめてのWebプログラミング入門 https://www.amazon.co.jp/dp/4844332953
PHPしっかり入門教室 使える力が身につく、仕組みからわかる。 https://www.amazon.co.jp/dp/4798153370
最初に「読む」PHP https://www.amazon.co.jp/dp/4883379019
PHPサーバーサイドプログラミングパーフェクトマスター https://www.amazon.co.jp/dp/4798044865
みるみるPHPがわかる本 https://www.amazon.co.jp/dp/4798026026
PHPプログラミングの教科書 https://www.amazon.co.jp/dp/B00GJGOF4G

初めてのPHP https://www.amazon.co.jp/dp/4873117933

以下覚え書き
・とりあえず「物理でも電子でもいいけど、購入可能なもの」除く古本
・「プログラム自体の初心者」を仮想ターゲットとする、ので、「変数は?」「環境構築」とかその辺が必須
・HTMLは理解している前提
・「PHP5 なのか 7なのか」は、初心者本的にはさほど重要事項ではない気がする(不要ではないけど、後回しでも大体足りると思われ)
・関数までは必須。classは、欲しいけど、ページ数的に「十全にやる」のはまぁ難しいと思われるので一端気にしない
・セキュリティ的にアウトなのは過敏に反応したい
・記述の嘘、誤解を招きやすいもの、コードサンプルの不備にも過敏に反応したい
・あとは、先に「評価軸」を作成してから、一気にチェック、かなぁ

購入は年内。
実施は年明け……には頑張れるといいなぁ。

0000-00-00 と PHP:実装側の考察

さて。
とりあえず「0000-00-00を食い止める」を至上命題にする場合、必要なのが
・空文字でINSERTしない
ここ。
これを主眼にして、少し実装側の考察をしてみませう。

一つ、ガードとして
sql_modeに STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_DATE を入れて置く
という手があるのですが。データは入らないのですが「SQLでエラーになる」ので、やり方を間違えると若干困るので、もうちょっと頑張ってみましょう。

https://gallu.hatenadiary.jp/entry/2019/11/04/160833 でも書きましたが、Laravel には ConvertEmptyStringsToNull というMiddleware があります。
今回の件で考えると一件便利そう、ではあるのですが、LaravelのConvertEmptyStringsToNullは「全項目無差別」なので。

んと……端的には
・文字列のカラムがあって
・空文字でもいい(入力必須ではない)
ようなケースで、ConvertEmptyStringsToNullがonになっていると
・未入力
・空文字……が、ConvertEmptyStringsToNullによってNULLに変換される
・NULLをinsertしようとしてエラー(NOT NULL制約って、普通、入ってるよね?)
って感じになるので、無差別はちょっと嬉しくないの感じでございます。

となると、とりあえずやりたいのが
・「カラムが日付系の型のデータだけ」空文字ならNULLに変換したい
ってのが、とりあえず今回としては「よい落としどころ」になるのではないかなぁ、と思っています。

後は、カラムによっては「文字型であろうが"NULL許可で、空文字が入るくらいならNULLがいい"」ってケースが、おいちゃんは全く思いつかないし思い浮かばないのですが、もしかするとそういうケースが「ないとも限らない」のであろうなぁ、と、思われるので(可能性としては)。

大雑把には
・「対象になるカラム名の配列」を用意して
・そのカラムについては「空文字ならNULLに置換する」
ような処理を、良い感じの箇所に挟み込んでおくと、比較的「案配がよい」のではないかなぁ、と思われます。

「とりあえず」SlimLittleToolsでの実装予定

んで。最近、Slimが大変にお好みなので、つい SlimLittleTools / なんてぇものを作って、使っておりますが。
そこでは
・filterの追加: ConvertEmptyStringsToNull と同じようなもの、を、カラム単位で出来るように
・MackModelDetail の修正( make_model_detail.php の実態 ):「日付型のカラムの配列」を作成できるようにする
・Modelの修正: 「日付型のカラムの配列」については「空文字ならNULL」に変換するようにする:その変換が on/off 出来るようなスイッチ用のメソッドを追加(デフォルトは、一応 false(変換しない)にしておく)
なんて実装を考えてます。

あと、 https://gallu.hatenadiary.jp/entry/2019/11/04/160833 で書いた
> NULLをVALUESに与えると、NULLになる(DEFAULTの値にはならない)
この部分が微妙に気になって引っかかっているので。加えて
・Modelの修正: insertとupdateのタイミングで「値がNULLなら、keyごと削除」の処理を追加:その削除が on/off 出来るようなスイッチ用のメソッドを追加(デフォルトは、一応 true(削除する)にしておく)
を入れておこうかなぁ、と思ってます。

この辺はまだ、少し考察や検討の余地などもありそうなので。
ご意見などいただけると、反映できる、かもしれないので、よかったら是非。

0000-00-00 と PHP

MySQLの、(少なくとも一部では)悪名高き 0000-00-00 について、は、そこそこブログがあるのですが。
「それを、PHP(plain)とか(PHPの)フレームワークとかでどうやってるんだろ?」というのが、ざっくりググった限りだと案外と無かったので。
調査かてがて、備忘録として。

0000-00-00 について

まずは 0000-00-00 について。
いやまぁググればあちこちで出てくる「MySQLに固有の、奇異なデータ」なのですが。
端的には
・入れ方によって、日付型(DATE、DATETIME、TIMESTAMP)に入る事があるデータ
MySQL以外で 0000-00-00 が入るRDBはない(はず)
・NOT NULL制約が入っているカラムで「0000-00-00」があると、WHERE句の IS NULL、IS NOT NULLの双方で 0000-00-00が引っかかる
てなもの、になります。

http://nippondanji.blogspot.com/2018/05/mysqlzero-date.html
https://soudai.hatenablog.com/entry/2018/05/12/191050
http://sakaik.hateblo.jp/entry/20151227/mysql_date_null

など、言及されている所は少なからずあるか、と思います。
詳しくは、上述のBlogなど見て頂くほうが早いかなぁ、と思います。

少し細かくまとめると。

・カラムにNOT NULL制約がある時
 → 空文字('')をVALUESに与えると、 0000-00-00 になる: modeにSTRICT_TRANS_TABLESがある時はエラーが出る(Incorrect date value: '' for column)
 → NULLをVALUESに与えると、エラーになる(当たり前だ)
・カラムにNOT NULL制約がない時
 → 空文字('')をVALUESに与えると、 0000-00-00 になる: modeにSTRICT_TRANS_TABLESがある時はエラーが出る(Incorrect date value: '' for column)
 → NULLをVALUESに与えると、NULLになる(当たり前だ)

となります。
また
・カラムにNOT NULL制約があり、かつ、DEFAULTが定義されている時
 → 空文字('')をVALUESに与えると、 0000-00-00 になる: modeにSTRICT_TRANS_TABLESがある時はエラーが出る(Incorrect date value: '' for column)
 → NULLをVALUESに与えると、エラーになる(当たり前だ)
 → INSERTの対象カラムからそもそも削除すると、DEFAULTで定義された値になる(当たり前だ)
・カラムにNOT NULL制約がなく、かつ、DEFAULTが定義されている時
 → 空文字('')をVALUESに与えると、 0000-00-00 になる: modeにSTRICT_TRANS_TABLESがある時はエラーが出る(Incorrect date value: '' for column)
 → NULLをVALUESに与えると、NULLになる(DEFAULTの値にはならない)
 → INSERTの対象カラムからそもそも削除すると、DEFAULTで定義された値になる(当たり前だ)

となります。
ここ、後述でちょっと重要になるので、ポイントとして。

フレームワーク無し」で実装すると?

まぁ昨今だととかですかねぇ。古来だとか。
日付を入力してもらう時に、まぁ最終的には文字列で受け取るわけ、なのですが。
これが「必須入力」なら「日付のフォーマットに沿ってるかどうかを判断」とかでいけばよくて……個人的にはstrtotime()使うのが、楽で好みかなぁ。

$t = strtotime($_POST['日付'] ?? '');
if (false === $t) {
    // エラー処理
    return;
}
// else
$date = date('Y-m-d H:i:s', $t);

とかやるとざっくりとフォーマットが整うので、よく使います。
DateTimeとかDateTimeImmutableとかのクラスを使う場合(このチョイスならDateTimeImmutableのほうが好み)、エラー時は「例外を投げてくる」ので、書き方にちょっとだけ注意を払いましょう。

ただ、必須じゃない場合「空文字を許容」する必要がある、と思われるのですが。

$date = $_POST['日付'] ?? '';
if ('' !== $date) {
    $t = strtotime();
    if (false === $t) {
        // エラー処理
        return;
    }
    // else
    $date = date('Y-m-d H:i:s', $t);
}

上述で「パラメタの値を取得して」「SQL作成してINSERTとかUPDATEとか」やると、結局日付系のカラムで「VALUEが空文字なINSERT/UPDATE」が走るので、0000-00-00問題が、回避できないような気がするのですだよ……。

なお

$date = $_POST['日付'] ?? null;

ってやっても変わらない。だって「formには"日付"のnameアトリビュートが存在しているから、空文字が返ってくる事が十分に想起される」から。

PHPフレームワークはどうなってるんだろ?

と言うわけで様々なフレームワークを……試す根性がありませんでしたすみません orz
ので、現状多分「一番メジャーであろう」Laravelで実験。
とりあえずバージョンは…… 6.4.1 。あら。5.8系かと思ったんだけど、バージョン指定しないとこんな先のバージョンになるのか。
まぁ「新しいほうがより機能は洗練されてる」だろうから、あんまり気にしない(雑

すげぇ単純に
・テーブル作って
・form入りのテンプレート作って
・ざっくりとinsert
こんな感じ。

マニュアルのPageにも
https://readouble.com/laravel/5.8/ja/eloquent.html

        $flight = new Flight;
        $flight->name = $request->name;
        $flight->save();

ってあるので、おんなじようにやってみる……エラー。
どうもConvertEmptyStringsToNullというのがお邪魔しくさりやがってくださっているらしく
・ConvertEmptyStringsToNull Middleware によって「空文字ならnullに置換」される
・対象カラムがNOT NULL制約
・なので「NOT NULL制約の所にNULLぶち込んだから嫌がられる」
という単純構造。

一端 app/Http/Kernel.php に修正を入れて、ConvertEmptyStringsToNull をoff(ついでにTrimStringsもoffしたほうがいいんじゃねぇか疑惑があるんだが一端放置)。
「未入力ならそのまま空文字になる」ように修正して再度実行。

……予想通り 0000-00-00 が入りやがりました orz
この辺、Laravelでも特に「なんか対応」はしていないんだなぁ、と、実感。

今までよく引っかかってこなかったなぁ……と思うんだけど、まぁぶち合ったんだからどうにか考えませんとなぁ。

https://gallu.hatenadiary.jp/entry/2019/11/04/160910 で、実装について少し考察をしてみます。

おまけ:実験結果

mysql> show create table date_tests;
+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| date_tests | CREATE TABLE `date_tests` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`d_test` date NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci |
+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select id, d_test from date_tests;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
+----+------------+
2 rows in set (0.01 sec)

mysql> insert into date_tests(d_test) value('');
Query OK, 1 row affected, 1 warning (0.01 sec)

mysql> insert into date_tests(d_test) value(null);
ERROR 1048 (23000): Column 'd_test' cannot be null
mysql> select id, d_test from date_tests;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
+----+------------+
3 rows in set (0.00 sec)

mysql> ALTER TABLE date_tests MODIFY COLUMN `d_test` date ;
Query OK, 0 rows affected (0.15 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> insert into date_tests(d_test) value('');
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> insert into date_tests(d_test) value(null);
Query OK, 1 row affected (0.01 sec)

mysql> select id, d_test from date_tests;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 5 | NULL |
+----+------------+
5 rows in set (0.00 sec)

mysql> SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into date_tests(d_test) value('');
ERROR 1292 (22007): Incorrect date value: '' for column 'd_test' at row 1
mysql> insert into date_tests(d_test) value(null);
Query OK, 1 row affected (0.00 sec)

mysql> select id, d_test from date_tests;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 5 | NULL |
| 6 | 0000-00-00 |
| 7 | NULL |
+----+------------+
7 rows in set (0.00 sec)



mysql> select id, d_test from date_tests where d_test is null;
+----+--------+
| id | d_test |
+----+--------+
| 5 | NULL |
| 7 | NULL |
+----+--------+
2 rows in set (0.00 sec)

mysql> select id, d_test from date_tests where d_test is not null;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
+----+------------+
5 rows in set (0.00 sec)

mysql> ALTER TABLE date_tests MODIFY COLUMN `d_test` date NOT NULL;
Query OK, 5 rows affected (0.09 sec)
Records: 5 Duplicates: 0 Warnings: 0

mysql> select id, d_test from date_tests ;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
+----+------------+
5 rows in set (0.00 sec)

mysql> select id, d_test from date_tests where d_test is null;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
+----+------------+
4 rows in set (0.00 sec)

mysql> select id, d_test from date_tests where d_test is not null;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
+----+------------+
5 rows in set (0.00 sec)

mysql> ALTER TABLE date_tests MODIFY COLUMN `d_test` date DEFAULT '1970-1-1';
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> insert into date_tests(d_test) value('');
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> insert into date_tests(d_test) value(null);
Query OK, 1 row affected (0.00 sec)

mysql> select id, d_test from date_tests ;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
| 8 | 0000-00-00 |
| 9 | NULL |
+----+------------+
7 rows in set (0.00 sec)

mysql> ALTER TABLE date_tests MODIFY COLUMN `d_test` date NOT NULL DEFAULT '1970-1-1';
Query OK, 7 rows affected, 1 warning (0.02 sec)
Records: 7 Duplicates: 0 Warnings: 1

mysql> select id, d_test from date_tests ;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
| 8 | 0000-00-00 |
| 9 | 0000-00-00 |
+----+------------+
7 rows in set (0.01 sec)

mysql> insert into date_tests(d_test) value('');
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> insert into date_tests(d_test) value(null);
ERROR 1048 (23000): Column 'd_test' cannot be null
mysql> select id, d_test from date_tests ;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
| 8 | 0000-00-00 |
| 9 | 0000-00-00 |
| 10 | 0000-00-00 |
+----+------------+
8 rows in set (0.00 sec)

mysql> insert into date_tests(id) value(null);
Query OK, 1 row affected (0.01 sec)

mysql> select id, d_test from date_tests ;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
| 8 | 0000-00-00 |
| 9 | 0000-00-00 |
| 10 | 0000-00-00 |
| 11 | 1970-01-01 |
+----+------------+
9 rows in set (0.01 sec)