令和最新版LaravelCollective/htmlからspatie/laravel-htmlへの移行注意点
著者の主な実績
- 自称Laravelバリスタ
- Laravel歴9年
- Laravel5.1→8.x、8.x->10.xへのアップグレード主導
- laravel/framework へPR採用
概要
LaravelCollective/html
Laravel4.2まで、HTMLタグを生成するための、\\Form
クラスがファサードで提供されていました。
{{ Form::open(array('url' => 'foo/bar')) }}
//
{{ Form::close() }}
4.2当時の機能紹介(Readouble) より引用 (arrayを使った記述がPHP: >=5.4
であった当時を偲ばせますね)
こちらはLaravel5.0にてLaravel本体からは廃止され、コミュニティ主体のパッケージ LaravelCollective の一機能として提供されることになりました。
Laravel Collective HTML package is abandoned
2023年にパッケージの廃止が案内され、Laravel11以降はサポートされなくなりました。
spatie/laravel-html
上記の廃止記事でも移行先として案内されているのが laravel-html です。
{{ html()->form('PUT', '/post')->open() }}
{{ html()->email('email')->placeholder('Your e-mail address') }}
{{ html()->form()->close() }}
記述は変わりましたが、これまでの\\Form
クラスとほぼ同等の機能を提供しており、Laravel11以降のサポートも継続しています。
Laravel Shift
LaravelCollectiveからlaravel-htmlの移行を手助けしてくれるサービスとして Laravel Shift があります。
クラウド実行だけでなく、dockerを使ったローカル実行 もサポートしており情報ポリシー的にも導入しやすい選択肢となっています。
LaravelCollectiveからlaravel-htmlの移行パッケージである HTML Converter は、
記述当時(2025/05)、クラウド版が19USD、Docker版が29USDで使用できます。
Docker版の注意点
Dockerの実行環境があれば、案内に従えば簡単に変換できますが、1ライセンスにつき実行は1度きりになるため、成果物は慎重に取り扱いましょう。
移行注意点
Laravel Shiftによる変換を行っても、LaravelCollectiveとlaravel-htmlの微妙な違いなどから注意しなければならない点があります。
こちらは弊社プロダクトの移行で実際に問題となった項目の重大度を、
私の独断で★1(影響ほぼ無しでしょう)から★5(確実に問題になる)に分けて紹介します。
また先行事例の laravel: laravelcollective/html から spatie/laravel-html に引っ越す も読むことを推奨します。
★1 selectで値との比較法の変更
echo \Form::select("foo", [0 => "zero", 1 => "one"], "1.0") . "\n";
echo html()->select("foo", [0 => "zero", 1 => "one"], "1.0") . "\n";
// <select name="foo"><option value="0">zero</option><option value="1">one</option></select>
// <select name="foo" id="foo"><option value="0">zero</option><option value="1" selected="selected">one</option></select>
LaravelCollectiveは文字列にキャストしての比較で固定されていましたが、laravel-htmlではin_arrayを使った比較になっています。
そのため、PHP8.0での 文字列と数値の比較 と組み合わさり、checkedの挙動が変わることがあります。
BCMathなどで数値を文字列として扱っていると抵触する場合があります。
★2 SHIFTで変換するとdate系がinputになりフォーマットの問題が生じる
echo \Form::date("foo", Carbon::today()) . "\n";
echo html()->date("foo", Carbon::today()) . "\n";
echo html()->input("date", "foo", Carbon::today()) . "\n";
// <input autocomplete="off" name="foo" type="date" value="2025-05-15">
// <input type="date" name="foo" id="foo" value="2025-05-15" autocomplete="off">
// <input type="date" name="foo" id="foo" value="2025-05-15 00:00:00" autocomplete="off">
LaravelCollective、laravel-html共にdate
、datetime
、time
ではvalueにDateTime
を受け取ったときに特定のフォーマットへ変換します。
しかし、Laravel Shiftは\Form::date
をhtml()->input("date")
にしてしまうので、
DateTime
やCarbon
をvalueに渡している場合は、html()->date()
への修正またはvalue手動フォーマットが必要です。
★2 selectで多重ネスト配列に未対応
echo \Form::select("foo", ["a" => ["b" => "B"]]) . "\n";
echo html()->select("foo", ["a" => ["b" => "B"]]) . "\n";
echo \Form::select("foo", ["a" => ["b" => ["c" => ["d" => "D"]]]]) . "\n";
echo html()->select("foo", ["a" => ["b" => ["c" => ["d" => "D"]]]]) . "\n";
// <select name="foo"><optgroup label="a"><option value="b">B</option></optgroup></select>
// <select name="foo" id="foo"><optgroup label="a"><option value="b">B</option></optgroup></select>
// <select name="foo"><optgroup label="a"><optgroup label=" b"><optgroup label=" c"><option value="d"> D</option></optgroup></optgroup></optgroup></select>
// TypeError: htmlentities(): Argument #1 ($string) must be of type string, array given
LaravelCollective、laravel-html共にselect
へネストされた配列を渡すとoptgroup
にしてくれますが、
laravel-htmlは1ネストまでしか対応していません。
★2 monthなど一部メソッドが消失
LaravelCollectiveでは\Form::month
や\Form::week
がありますが、laravel-htmlにはありません。
Laravel Shiftでもスルーされるので、手動でhtml()->input()
に書き換えましょう。
★2 SHIFTの変換にて論理属性の指定が変換できない場合がある
echo \Form::text("foo", null, [$disabled ? "disabled" : null]) . "\n";
// <input disabled name="foo" type="text">
// <input name="foo" type="text">
LaravelCollectiveでは配列を使ってタグの属性を指定していましたが、上記の例のような論理属性を三項演算子で付与したりしているとLaravel Shiftでうまく書き換えられない場合があります。
★3 inputへidが自動で付与される
echo \Form::text("foo") . "\n";
echo \Form::label("bar") . "\n";
echo \Form::text("bar") . "\n";
echo html()->text("foo") . "\n";
// <input name="foo" type="text">
// <label for="bar">Bar</label>
// <input name="bar" type="text" id="bar">
// <input type="text" name="foo" id="foo">
LaravelCollectiveではlabel
タグが前にある場合のみinput
のid属性が付与されましたが、
laravel-htmlでは原則id属性が付与されます。
jQueryなどでid指定していると参照を奪ってしまう可能性があります。
★4 submitがinputタグからbuttonタグに変更
echo \Form::submit("foo") . "\n";
echo html()->submit("foo") . "\n";
// <input type="submit" value="foo">
// <button type="submit">foo</button>
LaravelCollectiveではsubmit
でinput
タグが生成されますが、laravel-htmlではbutton
タグが生成されます。
CSSやjQueryのセレクタが変わるほか、flex
スタイルを適用していると影響する場合があります。
★4 checkboxでnameが配列だとoldが効かない
echo \Form::checkbox("foo[]", "bar") . "\n";
echo html()->checkbox("foo[]", null, "bar") . "\n";
echo html()->checkbox("foo[]", in_array("bar", old("foo", [])), "bar") . "\n"; // fix
// <input checked="checked" name="foo[]" type="checkbox" value="bar">
// <input type="checkbox" name="foo[]" id="foo[]" value="bar">
// <input type="checkbox" name="foo[]" id="foo[]" value="bar" checked>
Laravelでは、nameに[]
を指定するとフォームリクエストを配列として受け取ることができます。
LaravelCollectiveはこの記法に対応していますが、laravel-htmlでは対応していません。
手動でvalueとしてoldを参照するなどの対策が考えられます。
★5 radioで値との比較法の変更
echo \Form::radio("foo") . "\n";
echo \Form::radio("foo", "") . "\n";
echo \Form::radio("foo", 0) . "\n";
echo html()->radio("foo", null) . "\n";
echo html()->radio("foo", null, "") . "\n";
echo html()->radio("foo", null, 0) . "\n";
// <input name="foo" type="radio" value="foo">
// <input name="foo" type="radio" value="">
// <input name="foo" type="radio" value="0">
// <input type="radio" name="foo" id="foo">
// <input type="radio" name="foo" id="foo_" value checked>
// <input type="radio" name="foo" id="foo_0" value="0" checked>
old
がnullまたは存在しないとき、valueとして0
または""(空文字)
を与えたときの挙動が異なります。
LaravelCollectiveは未チェックですが、laravel-htmlではoldと一致した判定となりチェック済になります。
★5 radioでcheckedがoldより優先される
echo old("foo") . "\n";
// bar
echo \Form::radio("foo", "bar") . "\n";
echo \Form::radio("foo", "bar", false) . "\n";
echo \Form::radio("foo", "baz", true) . "\n";
echo html()->radio("foo", null, "bar") . "\n";
echo html()->radio("foo", false, "bar") . "\n";
echo html()->radio("foo", true, "baz") . "\n";
echo html()->radio("foo", !is_null(old("baz")), "baz") . "\n"; // fix
// <input checked="checked" name="foo" type="radio" value="bar">
// <input checked="checked" name="foo" type="radio" value="bar">
// <input name="foo" type="radio" value="baz">
// <input type="radio" name="foo" id="foo_bar" value="bar" checked>
// <input type="radio" name="foo" id="foo_bar" value="bar" checked>
// <input type="radio" name="foo" id="foo_baz" value="baz" checked>
// <input type="radio" name="foo" id="foo_baz" value="baz">
radio
はoldと値が一致したときcheckedになります。
LaravelCollectiveはchecked
指定よりも上記の一致が優先されますが、
laravel-htmlはchecked
がtrueの場合、常にcheckedになります。
手動でold
を考慮してcheckdに渡す対策が考えられます。
★5 textareaのデフォルトサイズの変更
echo \Form::textarea("foo") . "\n";
echo html()->textarea("foo") . "\n";
// <textarea name="foo" cols="50" rows="10">bar</textarea>
// <textarea name="foo" id="foo">bar</textarea>
LaravelCollectiveはtextarea
に対してデフォルトでrows=10 cols=50
を与えます。
ブラウザ本来の値 はrows=2 cols=20
なので、かなり差があります。
まとめ
書き換えたページの動作検証は必須ですが、大半のケースはlaravel Shiftで自動書き換えできます。
Laravelのサポートポリシー では、10以前はセキュリティサポート含め打ち切られているため、
これらのツールを利用してLaravel12への移行を目指しましょう。
Discussion