Laravel 5.4: Class 'Form' not found エラーが出る

What?

  • illuminate/html を使って、フォームを作ろうとすると、Class 'Form' not found (View: /home/vagrant/Code/laravel/resources/views/articles/create.blade.php) エラーが出る

Why?

  • Laravel 5.2 から仕様がちょっと変わった
    • illuminate/html ではなく、 laravelcollective/htmlを使う
  • Collective\Html\HtmlServiceProvider::class,HTMLを大文字にしてた。。。

How to?

  • composer require laravelcollective/htmlする
$ composer require laravelcollective/html
Using version ^5.4 for laravelcollective/html
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
> Illuminate\Foundation\ComposerScripts::postUpdate
> php artisan optimize
Generating optimized class loader
The compiled services file has been removed.
laravel vagrant master *%$ composer dump-autoload -o
Generating optimized autoload files
  • config/app.php
'providers' => [
    ...
    Collective\HTML\HtmlServiceProvider::class,
    ...
],

...

'aliases' => [
    ...
    'Form' => Collective\Html\FormFacade::class,
    'Html' => Collective\Html\HtmlFacade::class,
    ...
],
  • ん?できない。
    • エラーが変わった
FatalThrowableError in ProviderRepository.php line 201:
Class 'Collective\HTML\HtmlServiceProvider' not found
  • なんでだよ。。。ちゃんとインストールしてるよ。マッピングされてるよ。。キャッシュもクリアしてオートロードできてるはずなんだよ。。
$ ls -la vendor/laravelcollective/html/src/HtmlServiceProvider.php
-rw-r--r-- 1 vagrant vagrant 1489 Mar  9 15:10 vendor/laravelcollective/html/src/HtmlServiceProvider.php
$ composer info | grep laravel
laravel/framework                     v5.4.15 The Laravel Framework.
laravel/tinker                        v1.0.0  Powerful REPL for the Laravel framework.
laravelcollective/html                v5.4.1  HTML and Form Builders for the Laravel Framework
$ grep -r HtmlServiceProvider vendor/composer/
vendor/composer/autoload_classmap.php:    'Collective\\Html\\HtmlServiceProvider' => $vendorDir . '/laravelcollective/html/src/HtmlServiceProvider.php',
vendor/composer/autoload_static.php:        'Collective\\Html\\HtmlServiceProvider' => __DIR__ . '/..' . '/laravelcollective/html/src/HtmlServiceProvider.php',
  • 3日悩んだ結果。
'providers' => [
    ...
    Collective\Html\HtmlServiceProvider::class,
    ...
],
  • 答え: HTMLじゃなくてHtmlな。。。
    • ディレクトリ名なので大文字小文字が厳密に判断される
      • composerを使って、オートロードしているので、以下と一緒じゃなきゃだめ
$ less vendor/composer/installed.json
        "autoload": {
            "psr-4": {
                "Collective\\Html\\": "src/"
            },

次、困ったときは順番にこれをやる

  • Issue composer require “laravelcollective/html”:“^5.3.0”.
  • Verify that the vendor/laravelcollective/html directory exists.
  • Verify that your composer.json file has been updated and there’s an entry for laravelcollective/html package in your dependencies (require block).
  • Add Collective\Html\HtmlServiceProvider::class to your providers array of config/app.php.
  • Add ‘Form’ => Collective\Html\FormFacade::class and ‘Html’ => Collective\Html\HtmlFacade::class to your aliases array.
  • Confirm that the API exists: Issue a php artisan tinker command and try playing with Form:: APIs. See if throws any errors.

知見

  • ハマったら、キャッシュクリアしたり、再インストールしたりする(今回あんま関係なかったけど)
$ composer dump-autoload
$ composer update
$ composer clear-cache
$ php artisan route:clear
$ php artisan cache:clear
$ php artisan config:clear
$ composer remove laravelcollective/html
$ composer require laravelcollective/html

参考

Dependency "laravel/framework" is also a root requirement, but is not explicitly whitelisted. Ignoring.の意味

What?

  • Dependency "laravel/framework" is also a root requirement, but is not explicitly whitelisted. Ignoring.と表示されて意味がわからん
$ composer remove laravelcollective/html
Dependency "laravel/framework" is also a root requirement, but is not explicitly whitelisted. Ignoring.
Dependency "laravel/framework" is also a root requirement, but is not explicitly whitelisted. Ignoring.
Dependency "laravel/framework" is also a root requirement, but is not explicitly whitelisted. Ignoring.
Dependency "laravel/framework" is also a root requirement, but is not explicitly whitelisted. Ignoring.
Dependency "laravel/framework" is also a root requirement, but is not explicitly whitelisted. Ignoring.
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 0 installs, 0 updates, 1 removal
  - Removing laravelcollective/html (v5.4.1)
Writing lock file
Generating autoload files
> Illuminate\Foundation\ComposerScripts::postUpdate
> php artisan optimize


  [Symfony\Component\Debug\Exception\FatalThrowableError]
  Class 'Collective\HTML\HtmlServiceProvider' not found


Script php artisan optimize handling the post-update-cmd event returned with error code 1

Removal failed, reverting ./composer.json to its original content.
  • 直訳: Dependency “laravel/framework” is also a root requirement, but is not explicitly whitelisted. Ignoring.

    • 依存関係にある"laravel/framework"はrootの必須項目です。しかし、明示的にホワイトリストに入っていません。無視します。
  • ホワイトリスト

    • Composerではパッケージ更新時に引数として与えられる依存パッケージリストをホワイトリストと呼んでいる
    • e.g. $ composer update hoge fuga piyo
  • つまり、今回指定されたのは、laravelcollective/htmlだけで、明示的にlaravel/frameworkが引数として指定されたわけではないので、laravel/frameworkは消さないよ。ということです。

composer removeとかupdateとかできない

What?

  • Class hogehoge not foundと表示されて、composerでライブラリ操作ができなくなる
$ composer remove illuminate/html
Dependency "laravel/framework" is also a root requirement, but is not explicitly whitelisted. Ignoring.
Dependency "laravel/framework" is also a root requirement, but is not explicitly whitelisted. Ignoring.
Dependency "laravel/framework" is also a root requirement, but is not explicitly whitelisted. Ignoring.
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 0 installs, 0 updates, 1 removal
  - Removing illuminate/html (v5.0.0)
Writing lock file
Generating autoload files
> Illuminate\Foundation\ComposerScripts::postUpdate
> php artisan optimize


  [Symfony\Component\Debug\Exception\FatalThrowableError]
  Class 'Collective\HTML\HtmlServiceProvider' not found


Script php artisan optimize handling the post-update-cmd event returned with error code 1

Removal failed, reverting ./composer.json to its original content.

How to

  • config/app.phpprovidersに使っていないものが書いてあるので、消す
'providers' => [
    ...
    // Collective\HTML\HtmlServiceProvider::class,
    ...
],
  • おっけー
$ composer remove illuminate/html
Package "illuminate/html" listed for update is not installed. Ignoring.
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
> Illuminate\Foundation\ComposerScripts::postUpdate
> php artisan optimize
Generating optimized class loader
The compiled services file has been removed.
$ composer require laravelcollective/html
Using version ^5.4 for laravelcollective/html
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing laravelcollective/html (v5.4.1) Downloading: 100%
Writing lock file
Generating autoload files
> Illuminate\Foundation\ComposerScripts::postUpdate
> php artisan optimize
Generating optimized class loader
The compiled services file has been removed.

How to その2

  • php artisan optimizeでコケてるので、--no-scripts オプションで実行後スクリプトを実行させない
$ composer remove --no-scripts laravelcollective/html
Package "laravelcollective/html" listed for update is not installed. Ignoring.
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
$ composer require --no-scripts laravelcollective/html
Using version ^5.4 for laravelcollective/html
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files

vim NERDTree 更新(リロード)の仕方

Why?

  • ファイルを更新したのにNERDTreeに反映されなくてつらい

How to

  • Tree上の更新したいディレクトリの上にカーソルを乗せて r を押す
  • 更新の進捗状況は一番下の行
    • NERDTree: Refreshing node. This could take a while... DONE と表示されればOK

Homestead をリロードしようとしたらエラーになる

What?

  • カフェに行ってPCを開いて、起動しっぱなしのHomesteadサーバにアクセスしたらエラー
  • vagrantからリロードしても以下のエラーが出て立ち上がらない
$ vagrant reload
==> homestead-7: Attempting graceful shutdown of VM...
==> homestead-7: Checking if box 'laravel/homestead' is up to date...
==> homestead-7: Clearing any previously set forwarded ports...
==> homestead-7: Clearing any previously set network interfaces...
The specified host network collides with a non-hostonly network!
This will cause your specified IP to be inaccessible. Please change
the IP or name of your host only network so that it no longer matches that of
a bridged or non-hostonly network.

Why?

  • 英語を読もう(雑訳)

    • ホストオンリーではないネットワークで、特定のホストネットワークが衝突してます
    • これは、あなたが設定したIPにアクセス出来ないということです
    • そのホストはブリッジされているもしくはホストオンリーではないネットワークにおいて、もうマッチしていないので、あなたのホストオンリーネットワークのIPか名前を変えてください。
  • つまりは、外で作業を初めて、今のIP(homesteadのデフォルトIP: 192.168.10.10)が他の人と被ったから

Solution

  • .homestead/Homestead.yaml
ip: "192.168.10.10"

ip: "192.168.10.11"

とかに変更する。

  • /etc/hosts も変更することを忘れずに

Laravel 5.4: Model, Controller, Viewワークフロー

Why?

  • Articleの一覧、詳細を作ることで、一通りのModel, Controller, Viewワークフローを流す

How to

Articleの一覧を作る

  • app/Http/routes.phpにRoutingを追加
Route::get('articles', 'ArticlesController@index');
  • Controllerを追加 (laravel 5.2から–plainオプションはデフォルトになったので不要)
    • app/Http/Controllers/ArticlesController.phpが追加される
$ php artisan make:controller ArticlesController
  • app/Http/Controllers/ArticlesController.phpにindexメソッドを追加 (まずはviewに渡さずにプレーンに表示)
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Article;

class ArticlesController extends Controller
{
    public function index() {
        $articles = Article::all();
        return $articles;
    }
}
  • プレーンなレスポンスが帰ってくる
[
    {
        id: 1
        title: "My Update First Article"
        body: "Lorem ipsum"
        ...
    }
]
  • app/Http/Controllers/ArticlesController.phpViewからViewに渡す
    public function index() {
        $articles = Article::all();
        return view('articles.index', compact('articles'));
    }
  • View resources/views/articles/index.blade.php を追加
@extends('app')

@section('content')
  <h1>Articles</h1>
  <hr />
  @foreach ($articles as $article)
    <article>
      <h2>{{ $article->title }}</h2>
      <div class="body">{{ $article->body }}</div>
    </article>
  @endforeach
@stop

Articleの詳細を作る

  • app/Http/routes.phpにRoutingを追加
Route::get('articles/{id}', 'ArticlesController@show');
  • app/Http/Controllers/ArticlesController.phpにshowメソッドを追加
    public function show($id) {
        $article = Article::findOrFail($id);
        return view('articles.show', compact('article'));
    }
  • View resources/views/articles/show.blade.php を追加
@extends('app')

@section('content')
  <h1>{{ $article->title }}</h1>
  <article>
    <div class="body">{{ $article->body }}</div>
  </article>
@stop

一覧と詳細ページを繋げる

  • resources/views/articles/show.blade.phpにリンク追加
@extends('app')

@section('content')
  <h1>{{ $article->title }}</h1>
  <article>
    <div class="body">{{ $article->body }}</div>
  </article>
  <a href="/articles/">&lt; Back to list</a>
@stop
  • resources/views/articles/index.blade.phpにリンク追加
@extends('app')

@section('content')
  <h1>Articles</h1>
  <hr />
  @foreach ($articles as $article)
    <article>
      <h2><a href="/articles/{{ $article->id }}">{{ $article->title }}</h2></a>
      <div class="body">{{ $article->body }}</div>
    </article>
  @endforeach
@stop

参考

Laravel 5.4: Eloquent ORMを使ってみる

What?

  • Eloquent ORM: Laravel5にデフォルトで組み込まれているORマッパー
  • RailsのActive Recodeみたいに使える

How to

DBにモデルを保存

  • モデルを作る -> app/Article.php ができている
$ php artisan make:model Article
$ cat app/Article.php
<?php namespace App;
 
use Illuminate\Database\Eloquent\Model;
 
class Article extends Model {
 
    //
 
}
  • モデルができているかtinkerで確認
$ php artisan tinker

>>> $article = new App\Article;                                                                      => App\Article {#668}
  • モデルを使う (オブジェクトを作って、値を入れる)
>>> $article = new App\Article;
=> App\Article {#673}
>>> $article->title = 'My First Article';
=> "My First Article"
>>> $article->body = 'Lorem ipsum';
=> "Lorem ipsum"
>>> $article->published_at = Carbon\Carbon::now();
=> Carbon\Carbon {#682
     +"date": "2017-03-04 11:53:09.134952",
     +"timezone_type": 3,
     +"timezone": "UTC",
   }
>>> $article;
=> App\Article {#673
     title: "My First Article",
     body: "Lorem ipsum",
     published_at: Carbon\Carbon {#682
       +"date": "2017-03-04 11:53:09.134952",
       +"timezone_type": 3,
       +"timezone": "UTC",
     },
   }
  • 作ったオブジェクトをセーブする
>>> App\Article::all()->toArray();
=> []
>>> $article->save();
=> true
>>> App\Article::all()->toArray();
=> [
     [
       "id" => 1,
       "title" => "My First Article",
       "body" => "Lorem ipsum",
       "created_at" => "2017-03-04 11:53:44",
       "updated_at" => "2017-03-04 11:53:44",
       "published_at" => "2017-03-04 11:53:09",
     ],
   ]
$ mysql --user=homestead --password=secret homestead
mysql> select * from articles\G;
*************************** 1. row ***************************
          id: 1
       title: My First Article
        body: Lorem ipsum
  created_at: 2017-03-04 11:53:44
  updated_at: 2017-03-04 11:53:44
published_at: 2017-03-04 11:53:09
1 row in set (0.00 sec)

DBからモデルを読み込みアップデートしてみる

  • tinker でDBから情報を1行読み込んで、更新後、セーブする
>>> $article = App\Article::all()->first();
=> App\Article {#689
     id: 1,
     title: "My First Article",
     body: "Lorem ipsum",
     created_at: "2017-03-04 11:53:44",
     updated_at: "2017-03-04 11:53:44",
     published_at: "2017-03-04 11:53:09",
   }
>>> $article->title = 'My First Article Updated';
=> "My First Article Updated"
>>> $article->save();                                                                                => true
  • DBを確認
mysql> select * from articles\G;
*************************** 1. row ***************************
          id: 1
       title: My First Article Updated
        body: Lorem ipsum
  created_at: 2017-03-04 11:53:44
  updated_at: 2017-03-04 12:09:32
published_at: 2017-03-04 11:53:09
1 row in set (0.00 sec)

色々な方法でデータを検索する

>>> App\Article::find(1);
=> App\Article {#684
     id: 1,
     title: "My First Article Updated",
     body: "Lorem ipsum",
     created_at: "2017-03-04 11:53:44",
     updated_at: "2017-03-04 12:09:32",
     published_at: "2017-03-04 11:53:09",
   }
>>> App\Article::where('body', 'Lorem ipsum')->get();
=> Illuminate\Database\Eloquent\Collection {#656
     all: [
       App\Article {#692
         id: 1,
         title: "My First Article Updated",
         body: "Lorem ipsum",
         created_at: "2017-03-04 11:53:44",
         updated_at: "2017-03-04 12:09:32",
         published_at: "2017-03-04 11:53:09",
       },
     ],
   }
>>> App\Article::where('body', 'Lorem ipsum')->first();
=> App\Article {#693
     id: 1,
     title: "My First Article Updated",
     body: "Lorem ipsum",
     created_at: "2017-03-04 11:53:44",
     updated_at: "2017-03-04 12:09:32",
     published_at: "2017-03-04 11:53:09",
   }

createメソッドを使ってみる

  • createメソッドはまとめて値を入力できるので便利

    • ただし、ユーザの突っ込んできた値の全てを受け入れると危険
    • $fillableで、createでアサインできる値のホワイトリストを指定できる
    • $guardedで、createでアサインできる値のブラックリストを指定できる
  • App\Article::create()でまとめて作ろうとするとMassAssignmentExceptionエラーが出る

>>> $article = App\Article::create(['title' => 'New Article', 'body' => 'Lorem ipsum', 'published_at' => Carbon\Carbon::now()]);
Illuminate\Database\Eloquent\MassAssignmentException with message 'title'
  • app/Article.phpモデルを修正する
<?php namespace App;
 
use Illuminate\Database\Eloquent\Model;
 
class Article extends Model {
 
    protected $fillable = [ // 
        'title',
        'body',
        'published_at'
    ];
 
}
  • ん?まだ、MassAssignmentExceptionエラーが出る
>>> $article = App\Article::create(['title' => 'New Article', 'body' => 'Lorem ipsum', 'published_at' => Carbon\Carbon::now()]);
Illuminate\Database\Eloquent\MassAssignmentException with message 'title'
  • tinkerを立ち上げなおしてリトライ。できた。
>>> $article = App\Article::create(['title' => 'New Article', 'body' => 'Lorem ipsum', 'published_at' => Carbon\Carbon::now()]);
=> App\Article {#670
     title: "New Article",
     body: "Lorem ipsum",
     published_at: Carbon\Carbon {#661
       +"date": "2017-03-04 12:17:28.029855",
       +"timezone_type": 3,
       +"timezone": "UTC",
     },
     updated_at: "2017-03-04 12:17:28",
     created_at: "2017-03-04 12:17:28",
     id: 2,
   }

参考