laravel 「Call to a member function メソッド名 on null」というエラーが出た

# 目的

- 「Call to a member function メソッド名() on null」というエラーが出たので筆者の場合の解決方法をまとめる

# 環境

- ハードウェア環境

| 項目 | 情報 |
| --- | --- |
| 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.11 | Homebrewを用いてこちらの方法で導入→[Mac HomebrewでPHPをインストールする](https://qiita.com/miriwo/items/cd54077aad7e139cf518) |
| Laravel バージョン | 6.X | commposerを用いてこちらの方法で導入→[Mac Laravelの環境構築を行う](https://qiita.com/miriwo/items/adaaaeecae69d6392e08) |
| MySQLバージョン | 8.0.21 for osx10.15 on x86_64 | Homwbrewを用いてこちらの方法で導入→[Mac HomebrewでMySQLをインストールする](https://qiita.com/miriwo/items/ef6a064bc545fbea29d9) |

# エラー

- 下記のエラーがブラウザとログに出力された。

    ```
    Call to a member function メソッド名() on null 
    ```

- このエラーは`クラス名->メソッド名()`と記載しメソッドを呼び出している部分の`クラス名`部分がnullであり「saveFile()なんてメソッド無いですよ」という時に出力されるエラーである。

# 具体的なソースとエラー

- エラーの出たコントローラクラスの内容を下記に記載する。

    ```NoticeController.php
    <?php
    
    namespace App\Http\Controllers;
    
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Mail;
    use App\Mail\AppMail;
    use App\Services\MailAttachmentService;
    
    class NoticeController extends Controller
    {
        /**
         * @var MailAttachmentService
         */
        private $mailAttachmentService;
    
        public function __construct(MailAttachmentService $mailAttachmentService)
        {

        }
    
        public function index()
        {
            return view('notices.index');
        }
    
        public function mailMake()
        {
            return view('notices.mails.make');
        }
    
        public function mailConfirm(Request $request)
        {
            $postData = $request->all();
            
            if (isset($postData['file'])) {
                $postData['putFileInfo'] = $this->mailAttachmentService->saveFile($postData['file']);
            }
    
            $viewData = [
                'postData' => $postData
            ];
    
            return view('notices.mails.confirm', ['viewData' => $viewData]);
        }
    
        public function mailSend(Request $request)
        {
            $postData = $request->all();
            Mail::to('test@example')->send(new AppMail($postData));
            return redirect(route('notice.index'));
        }
    }
    ```

- 出力されたエラー

    ```
    Call to a member function saveFile() on null
    ```

# 悪さをしている部分

- コントローラクラスの中のmailConfirm()メソッドの中に記載されている。`$postData['putFileInfo'] = $this->mailAttachmentService->saveFile($postData['file']);`でエラーが発生している。
- このsaveFile()メソッドがnullから呼ばれていますよといったエラー内容である。
- つまり`$this->mailAttachmentService`がnullということになるので当該メソッドにクラスを格納（依存注入）している部分を確認してみる。
- 下記に__construct()メソッドを抜粋して記載する。

    ```NoticeController.php
    public function __construct(MailAttachmentService $mailAttachmentService)
    {

    }
    ```

- __construct()メソッドの中で$this->mailAttachmentService（このクラスに新たなメソッド定義）に外部クラスを格納（依存注入）し忘れている事がわかった。（自身のクラスに新たにメソッドを定義して外部のクラスを注入することによりクラス間を疎結合にする。）
- 下記のように修正したら正常に動作した。

    ```NoticeController.php
    <?php
    
    namespace App\Http\Controllers;
    
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Mail;
    use App\Mail\AppMail;
    use App\Services\MailAttachmentService;
    
    class NoticeController extends Controller
    {
        /**
         * @var MailAttachmentService
         */
        private $mailAttachmentService;
    
        public function __construct(MailAttachmentService $mailAttachmentService)
        {
            // 下記を追記
            $this->mailAttachmentService = $mailAttachmentService;
        }
    
        public function index()
        {
            return view('notices.index');
        }
    
        public function mailMake()
        {
            return view('notices.mails.make');
        }
    
        public function mailConfirm(Request $request)
        {
            $postData = $request->all();
            
            if (isset($postData['file'])) {
                $postData['putFileInfo'] = $this->mailAttachmentService->saveFile($postData['file']);
            }
    
            $viewData = [
                'postData' => $postData
            ];
    
            return view('notices.mails.confirm', ['viewData' => $viewData]);
        }
    
        public function mailSend(Request $request)
        {
            $postData = $request->all();
            Mail::to('test@example')->send(new AppMail($postData));
            return redirect(route('notice.index'));
        }
    }
    ```

- 追記後に動作を確認したらエラーは出ず正常に動作した。