Laravelが本番環境で上手く動かなかったとき

# 今回、Laravelを本番環境に移した時に起きたこと

 まずローカルのLaravelで上手く動いていたのを確認してから本番環境に移すと、移した途端エラーが発生しました。

一言でいうと、原因は全て
**ローカル**だと寛容に動いてくれるのに**本番環境**だと厳しくコードを見られて動かない
というのが原因でした。

# 命名規則のエラー

まず、Laravelの`ルート\storage\logs\laravel.log`を確認すると以下のようなエラーが発生しました。
```
Class "App\Methods\UserList" not found  
at /app/Http/Controllers/ApiController.php:129
```
エラーの内容は
App\Methods\UserList クラスが存在しない
つまり、ApiController の中で以下のような処理をしているはずです
```
use App\Methods\UserList;
...

$userList = new UserList(); // ← ここでクラッシュ！
```
でも Laravel はその UserList クラスを 読み込めない（存在していない or オートロードできない） というエラーです。

**次に、以下のコードで実行すると**
```
composer dump-autoload
```

```
[XXX123@XXXXXX.com]$ composer dump-autoload
Generating optimized autoload files
Class App\Methods\PrintData located in ./app/Methods/printData.php does not comply with psr-4 autoloading standard. Skipping.
Class App\Methods\UserList located in ./app/Methods/userList.php does not comply with psr-4 autoloading standard. Skipping.
Class App\Models\DeshioKiroku located in ./app/Models/Deshiokiroku.php does not comply with psr-4 autoloading standard. Skipping.
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi

   INFO  Discovering packages.  

  anourvalar/eloquent-serialize ............................................. DONE
  blade-ui-kit/blade-heroicons .............................................. DONE
  blade-ui-kit/blade-icons .................................................. DONE
  filament/actions .......................................................... DONE
  filament/filament ......................................................... DONE
  filament/forms ............................................................ DONE
  filament/infolists ........................................................ DONE
  filament/notifications .................................................... DONE
  filament/support .......................................................... DONE
  filament/tables ........................................................... DONE
  filament/widgets .......................................................... DONE
  kirschbaum-development/eloquent-power-joins ............................... DONE
  laravel/pail .............................................................. DONE
  laravel/sail .............................................................. DONE
  laravel/sanctum ........................................................... DONE
  laravel/tinker ............................................................ DONE
  livewire/livewire ......................................................... DONE
  nesbot/carbon ............................................................. DONE
  nunomaduro/collision ...................................................... DONE
  nunomaduro/termwind ....................................................... DONE
  ryangjchandler/blade-capture-directive .................................... DONE
  spatie/laravel-permission ................................................. DONE
  tymon/jwt-auth ............................................................ DONE

> @php artisan filament:upgrade
  ⇂ public/js/filament/forms/components/color-picker.js  
  ⇂ public/js/filament/forms/components/date-time-picker.js  
  ⇂ public/js/filament/forms/components/file-upload.js  
  ⇂ public/js/filament/forms/components/key-value.js  
  ⇂ public/js/filament/forms/components/markdown-editor.js  
  ⇂ public/js/filament/forms/components/rich-editor.js  
  ⇂ public/js/filament/forms/components/select.js  
  ⇂ public/js/filament/forms/components/tags-input.js  
  ⇂ public/js/filament/forms/components/textarea.js  
  ⇂ public/js/filament/tables/components/table.js  
  ⇂ public/js/filament/widgets/components/chart.js  
  ⇂ public/js/filament/widgets/components/stats-overview/stat/chart.js  
  ⇂ public/js/filament/filament/app.js  
  ⇂ public/js/filament/filament/echo.js  
  ⇂ public/js/filament/notifications/notifications.js  
  ⇂ public/js/filament/support/support.js  
  ⇂ public/css/filament/forms/forms.css  
  ⇂ public/css/filament/support/support.css  
  ⇂ public/css/filament/filament/app.css  

   INFO  Successfully published assets!  

   INFO  Configuration cache cleared successfully.  

   INFO  Route cache cleared successfully.  

   INFO  Compiled views cleared successfully.  

   INFO  Successfully upgraded!  

Generated optimized autoload files containing 8082 classes 
```
# ❌ 問題：PSR-4オートロード規約違反
```
Class App\Methods\PrintData located in ./app/Methods/printData.php does not comply with psr-4 autoloading standard. Skipping.
Class App\Methods\UserList located in ./app/Methods/userList.php does not comply with psr-4 autoloading standard. Skipping.
```
つまり、クラス名とファイル名の大文字小文字が一致していないため、Laravel が読み込んでくれないことが原因でした。

**実際に保存していたファイル名は`userList.php`と`printData.php`でした。**

ローカルのLaravelではそれでも問題なく動くのですがXserverの場合だとそこを厳しく見られたので注意が必要でした。

# フィラメントの管理画面が上手く表示されない原因
ローカルのLaravelでは問題なく動くのに本番環境ではログインした後に何も表示されないという現象が起きました。

最初は、ローカルで動かすときと比べて、パスがズレているのだと思い込み、自分では原因を特定出来ませんでした。
#### ルートの.htacceass
```
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteRule ^(.*)$ public/$1 [QSA,L]
</IfModule>
```

ですが、実際は、認証に使っているModelのファイルの記述が古い書き方だったことが原因のようでした。
#### ルート\app\Models\User.php
```User.php
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Database\Eloquent\Model;

class User extends Authenticatable implements JWTSubject
{
    use Notifiable;

    protected $fillable = ['name', 'login_code', 'password', 'email', 'staff_id', 'is_admin'];

    protected $hidden = ['password'];

    // モデルの作成時にデフォルト値を設定
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($user) {
            // is_adminが設定されていない場合、デフォルトで管理者権限を付与
            if (!isset($user->is_admin)) {
                $user->is_admin = 1;
            }
        });
    }

    // ✅ JWT の識別子を取得
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    // ✅ JWT に追加するカスタムクレーム
    public function getJWTCustomClaims()
    {
        return [];
    }

    /**
     * Filament へのアクセス権限を判定するメソッド
     *
     * @return bool
     */
    public function canAccessFilament(): bool
    {
        return $this->is_admin === 1; // `is_admin` が 1 の場合のみ Filament にアクセス可能
    }
}
```

## 修正後
```User.php
<?php

namespace App\Models;

use Filament\Panel;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Filament\Models\Contracts\FilamentUser;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Database\Eloquent\Model;

class User extends Authenticatable implements JWTSubject, FilamentUser
{
    use Notifiable;

    // laravel_db接続を指定
    protected $connection = 'mysql';

    protected $fillable = ['name', 'login_code', 'password', 'email', 'staff_id', 'is_admin'];

    protected $hidden = ['password'];

    // モデルの作成時にデフォルト値を設定
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($user) {
            // is_adminが設定されていない場合、デフォルトで管理者権限を付与
            if (!isset($user->is_admin)) {
                $user->is_admin = 1;
            }
        });
    }

    // ✅ JWT の識別子を取得
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    // ✅ JWT に追加するカスタムクレーム
    public function getJWTCustomClaims()
    {
        return [];
    }

    /**
     * Filament へのアクセス権限を判定するメソッド
     *
     * @return bool
     */
    public function canAccessPanel(Panel $panel): bool
    {
       return $this->is_admin === 1; // `is_admin` が 1 の場合のみ Filament にアクセス可能

    }
}
```

# ✅ 主な変更点の解説
### ① FilamentUser インターフェースの追加
```
use Filament\Models\Contracts\FilamentUser;
```
**目的：**
Filament v3 以降では、ユーザーモデルが管理画面（Filament）にアクセスできるかを判定するために、FilamentUser インターフェースの実装が必要になりました。

### ② canAccessFilament() → canAccessPanel(Panel $panel) へ変更

**変更前：**
```
public function canAccessFilament(): bool
```
**変更後**
```
public function canAccessPanel(Panel $panel): bool
```
**変更理由：**
Filament の仕様変更により、ユーザーがアクセスできる「パネル（Panel）」が複数存在することを想定した構成に変わったため、どの Panel にアクセスするかを引数として受け取る必要が出てきました。

### ③ protected $connection = 'mysql'; の追加
```
protected $connection = 'mysql';
```
**目的：**
Laravel では、デフォルト以外のデータベース接続をモデル単位で指定することができます。
今回はデフォルトのデータベース以外にもう一つのデータベースとの連携をUsersテーブルにさせるために
デフォルトのデータベースのUserテーブルを使うことを
User モデルに対して mysql 接続を明示的に指定しています
（laravel_db がデフォルトのデータベース）

### ④protected $connection = 'mysql';の `mysql`はどこで設定した文字列なのか
`ルート\config\database.php` に記述した設定を使用しています。

```database.php
<?php

use Illuminate\Support\Str;

return [


    'default' => env('DB_CONNECTION', 'mysql'),


    'connections' => [

        'sqlite' => [
            'driver' => 'sqlite',
            'url' => env('DB_URL'),
            'database' => env('DB_DATABASE', database_path('database.sqlite')),
            'prefix' => '',
            'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
            'busy_timeout' => null,
            'journal_mode' => null,
            'synchronous' => null,
        ],

        // ✅ デフォルトの MySQL 接続（LaravelのメインDB）
        'mysql' => [
            'driver' => 'mysql',
            'host' => env('XXXXXXX', '127.0.0.1'),
            'port' => env('XXXXXXX', '3306'),
            'database' => env('XXXXX_DB_DATABASE', 'XXXXXXX'),
            'username' => env('XXXXX_DB_USERNAME', 'XXXX'),
            'password' => env('XXXX_DB_PASSWORD', 'XXXX'),
            'charset' => env('XXXXX_DB_CHARSET', 'utf8mb4'),
            'collation' => env('XXXXXX_DB_COLLATION', 'utf8mb4_unicode_ci'),
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],
            // ✅ もう一つのMySQL 接続（外部のデータベース）
        'XXXXXXXX' => [
            'driver' => 'mysql',
            'host' => env('XXXXXXX', '127.0.0.1'),
            'port' => env('XXXXXXX', '3306'),
            'database' => env('XXXXX_DB_DATABASE', 'XXXXXXX'),
            'username' => env('XXXXX_DB_USERNAME', 'XXXX'),
            'password' => env('XXXX_DB_PASSWORD', 'XXXX'),
            'charset' => env('XXXXX_DB_CHARSET', 'utf8mb4'),
            'collation' => env('XXXXXX_DB_COLLATION', 'utf8mb4_unicode_ci'),
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],

        'mariadb' => [
            'driver' => 'mariadb',
            'url' => env('DB_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'laravel'),
            'username' => env('DB_USERNAME', 'root'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => env('DB_CHARSET', 'utf8mb4'),
            'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
        ],

        'pgsql' => [
            'driver' => 'pgsql',
            'url' => env('DB_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '5432'),
            'database' => env('DB_DATABASE', 'laravel'),
            'username' => env('DB_USERNAME', 'root'),
            'password' => env('DB_PASSWORD', ''),
            'charset' => env('DB_CHARSET', 'utf8'),
            'prefix' => '',
            'prefix_indexes' => true,
            'search_path' => 'public',
            'sslmode' => 'prefer',
        ],

        'sqlsrv' => [
            'driver' => 'sqlsrv',
            'url' => env('DB_URL'),
            'host' => env('DB_HOST', 'localhost'),
            'port' => env('DB_PORT', '1433'),
            'database' => env('DB_DATABASE', 'laravel'),
            'username' => env('DB_USERNAME', 'root'),
            'password' => env('DB_PASSWORD', ''),
            'charset' => env('DB_CHARSET', 'utf8'),
            'prefix' => '',
            'prefix_indexes' => true,
            // 'encrypt' => env('DB_ENCRYPT', 'yes'),
            // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
        ],

    ],

    /*
    |--------------------------------------------------------------------------
    | Migration Repository Table
    |--------------------------------------------------------------------------
    |
    | This table keeps track of all the migrations that have already run for
    | your application. Using this information, we can determine which of
    | the migrations on disk haven't actually been run on the database.
    |
    */

    'migrations' => [
        'table' => 'migrations',
        'update_date_on_publish' => true,
    ],

    /*
    |--------------------------------------------------------------------------
    | Redis Databases
    |--------------------------------------------------------------------------
    |
    | Redis is an open source, fast, and advanced key-value store that also
    | provides a richer body of commands than a typical key-value system
    | such as Memcached. You may define your connection settings here.
    |
    */

    'redis' => [

        'client' => env('REDIS_CLIENT', 'phpredis'),

        'options' => [
            'cluster' => env('REDIS_CLUSTER', 'redis'),
            'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
        ],

        'default' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'username' => env('REDIS_USERNAME'),
            'password' => env('REDIS_PASSWORD'),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_DB', '0'),
        ],

        'cache' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'username' => env('REDIS_USERNAME'),
            'password' => env('REDIS_PASSWORD'),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_CACHE_DB', '1'),
        ],

    ],

];
```
下記の 'mysql'が先ほどの `Model/User.php` で指定した文字列になります。(デフォルトのデータベースは`mysql`に初期状態はなっている)

```
        'mysql' => [
            'driver' => 'mysql',
            .....
```
別の `✅ もう一つのMySQL 接続（外部のデータベース）`をしたい場合は `XXXXXXXX' => [`の `XXXXXXXX`を使用します。

## 外部のデータベース使用例
```Staff.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class StaffLaravelLink extends Model
{
    use HasFactory;
    // 'XXXXXXXX' という接続先を利用
    protected $connection = 'XXXXXXXX';

    protected $table = 'staff';
```
## `'host' => env('XXXXXXX', '127.0.0.1'), `の記述の意味
これは`.env`ファイルのキーがなければ右のデフォルト値を使用するという意味です
```
env('キー名', 'デフォルト値')
```

# 🛠 その他は同じ内容（JWT関連、fillableなど）
以下の記述は、旧コードと変更なしです：

getJWTIdentifier() と getJWTCustomClaims() → JWT認証に必要な定義

$fillable, $hidden → Eloquentモデルの基本

boot() 内の creating() → モデル作成時に is_admin にデフォルト値を設定する処理

# 🎯 まとめ
変更点	理由・背景
FilamentUser 実装	Filament v3 対応のため必要
canAccessFilament() → canAccessPanel(Panel $panel)	マルチパネル対応の新仕様
$connection = 'mysql'	データベース接続を明示的に指定
このように、LaravelやFilamentのアップデートに伴って、モデルの書き方も少しずつ変わっていきます。特に認証や管理画面に関する部分はバージョン依存が強いので、ドキュメントやリリースノートをこまめに確認するのが大切です。

参考：Filament v3 ドキュメント