Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Laravel 文字列変数によってオブジェクトのインスタンスを生成する方法

解決したいこと

Laravel/PHPのアプリケーションを開発している66歳の初心者です。
所定の文字列変数によって、オブジェクトのインスタンスを生成しようとして、過去の記事を参考にしてコードを記述しましたが、エラーになりうまく行きません。現在はやむを得ず、Switch-Caseで分岐させていますが、コード効率が良くありません。
ただしい記述方法を教えて頂けると大変助かります。

発生している問題・エラー

Error
Class "KlassExport" not found
http://localhost/backoffices/backup

該当するソースコード コメントアウトしてあるのはSwitch-Caseで対処している動作するコードです。

<?php

namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\KlassExport;
....中略....

class BackupController extends Controller
{

....中略....

    public function backup (Request $request) 
    // Excel file nameは DBのYYYYMMDD-HHMM_データベース略称.xlsx
    // リカバー時にはデータベース略称を自動判別して読み込む
    {
        $user = Auth::user();
        $data = $request->all();
        unset($data['_token']);
        $today = date ("Ymd-His");
        $name = Str::singular($data['select']);
        $callname = ucfirst($name) . "Export";
        $new = new $callname();
        return Excel::download ($new, $today . $name . ".xlsx");

        // switch ($data['select'])
        // {
        //     case 'klasses'     : return Excel::download (new KlassExport,       $today . "_klass.xlsx");
        //     case 'allshares'   : return Excel::download (new AllshareExport,    $today . "_allshare.xlsx");
        //     case 'allsharebases':return Excel::download (new AllsharebaseExport,$today . "_allsharebase.xlsx");
        //     case 'owners'      : return Excel::download (new OwnerExport,       $today . "_owner.xlsx");
        //     case 'transactions': return Excel::download (new TransactionExport, $today . "_transaction.xlsx");
        //     case 'contacts'    : return Excel::download (new ContactExport,     $today . "_contact.xlsx");
        //     case 'oppools'     : return Excel::download (new OppoolExport,      $today . "_oppool.xlsx");
        //     case 'opgrants'    : return Excel::download (new OpgrantExport,     $today . "_opgrant.xlsx");
        //     case 'alloptions'  : return Excel::download (new AlloptionExport,   $today . "_alloption.xlsx");
        //     case 'opowners'    : return Excel::download (new OpownerExport,     $today . "_opowner.xlsx");
        //     case 'shmeetings'  : return Excel::download (new ShmeetingExport,   $today . "_shmeeting.xlsx");
        //     case 'corpprofiles': return Excel::download (new CorpprofileExport, $today . "_corpprofile.xlsx");
        //     case 'voterecords' : return Excel::download (new VoteRecordExport,  $today . "_voterecord.xlsx");
        //     case 'users'       : return Excel::download (new UserExport,        $today . "_user.xlsx");
        //     case 'whole'       : return Excel::download (new WholeExport,       $today . "_whole.xlsx");
        //     default: return;
        // }
    }

自分で試したこと

下記の情報を参考にして、行く通りかのパターンを試したのですがいずれも同じエラーになります。
Switch-Caseで動作しているのですが、動作するコードを作ったら「リファクタリング」すべきとのベストプラクティスの記事を拝見して、色々書き直しを検討している中で教えて頂きたく、どうぞよろしくお願いいたします。

https://qiita.com/gorogoroyasu/items/d8a6e032eb4debb147ca
https://www.php.net/manual/ja/language.oop5.basic.php#language.oop5.basic.new

0 likes

2Answer

おそらくですが、名前空間が関係していると思います。

参考にされている記事では、中段からの
"では、クラスが複数ファイルにまたがり、namespace を使った時は?"
という部分です。

マニュアルでは、名前空間がある場合に動的アクセスするには次のように説明されています。

完全修飾名 (クラス名に名前空間プレフィックスをつけたもの) を使う必要があります。

名前空間なし
class classname
{
    function __construct()
    {
        echo __METHOD__,"\n";
    }
}

$class = 'classname';
$obj = new $class; // "classname::__construct"
名前空間あり
namespace namespacename;
class classname
{
    function __construct()
    {
        echo __METHOD__,"\n";
    }
}

// $class = 'classname'; // NG! Class "classname" not found
$class = '\namespacename\classname'; // OK!
$obj = new $class; // "namespacename\classname::__construct"

1Like

Comments

  1. @kats-nakazawa

    Questioner

    Blue32a様

    早速ご回答ありがとうございます。
    完全修飾名にしたところ、うまく行きました!


    実は質問を出す前に、一度名前空間のプレフィックスを付けて(つもりで)試してエラーが出ていましたが、改めて試したらうまく動作しました。最初の試行の際に、バックスラッシュをエスケープせずに、スペースを入れて回避したのが良くなかった様です。
    動作!!
    $name = Str::singular($data['select']);
    $callname = "App\Exports\\" . ucfirst($name) . "Export";
    $new = new $callname();
    return Excel::download ($new, $today . $name . ".xlsx");


    間違えていたプレフィックス

    $callname = "App\Exports\ " . ucfirst($name) . "Export";

    初心者の質問に丁寧にお答え頂き感謝申し上げます。
  2. 解決したようで良かったです。

    コードの綺麗さは動作に影響しませんが、開発後の保守作業に影響します。業務で使うコードは開発担当者よりも長生きしがちなので、コードの綺麗さに気を配ってリファクタリングするのは素晴らしいと思います。
    頑張ってください。

blue32a様

はい。おかげさまで解決し、すっきりした気分です。

今回の該当箇所以外にも、4か所ほどのコードがごっそり短縮できただけでなく、処理対象のテーブルの名前を配列で与えるだけで、スケーラブルに変更できるようになりました。実際はこの効果の方が大きい気がします。

クラスタリング対象は他にも色々ネット情報を参考にしながら手探りで、時間がかかりますが、進めていこうと思います。
どうもありがとうございました。

0Like

Your answer might help someone💌