Laravel 5.1 - 根据字符串动态创建类对象

14

我希望能够根据来自URL参数的字符串创建类的对象。

例如:

 http://localhost/CSWeb/api/search/Slideshare

在上述的URL中,Slideshare是一个参数,它被传递给apiController->indexAction方法。

slideshare.php类。

<?php

namespace App\Http\API;

class slideshare 
{

    public function index()
    {
         return 'any data'; 

    }

}

apiController.php

namespace App\Http\Controllers\API;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Auth;
use App\Http\API\Slideshare;

class apiController extends Controller
{

    public function index($source)
    {
        $controller=  new $source;
        return $controller->index();
        // if i change code to  $controller=  new Slideshare; it works fine
    }

}

当我使用字符串参数创建类对象时,Laravel出现错误:

apiController.php的第17行发生致命错误:类'Slideshare'未找到

如果我将代码改为

$controller=  new Slideshare; it works fine

提前感谢您。

1个回答

48
在使用字符串创建PHP对象时,您必须提供类的完全限定名称(也就是包括命名空间)。因此,您应该像这样编写代码:
$className = 'App\\Http\\API\\' . $source;

$controller =  new $className;

return $controller->index();

如果你确定你想要实例化的类与你的代码位于相同的命名空间中,还有另一种方法可以实现,你可以使用:
$className = __NAMESPACE__ . '\\' . $source;

$controller =  new $className;

return $controller->index();

通过工厂设计模式可以更详细地实现相同的结果。基本上,您创建一个负责实例化元素的类,并将实际创建这些对象的任务委托给该类。大致如下所示:
class Factory {
  function __construct ( $namespace = '' ) {
     $this->namespace = $namespace;
  }

  public function make ( $source ) {
    $name = $this->namespace . '\\' . $source;

    if ( class_exists( $name ) ) {
      return new $name();
    }
  }
}


$factory = new Factory( __NAMESPACE__ );

$controller = $factory->make( $source );

这种方法的优点是创建对象的责任现在落在了工厂上,如果你需要进行更改,比如允许别名、增加一些额外的安全措施或其他任何事情,你只需要在一个地方修改代码,只要类的签名保持不变,你的代码就能继续正常工作。
关于工厂模式的一个有趣教程: http://culttt.com/2014/03/19/factory-method-design-pattern/

Source: https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.new http://php.net/manual/en/language.namespaces.nsconstants.php


2
啊,找到了。+1。“如果使用包含类名称的字符串与new一起使用,则会创建该类的新实例。如果该类在命名空间中,则在执行此操作时必须使用其完全限定名称。” - ceejayoz
@Scorch 谢谢。完美的答案,它很好地解决了我的问题。你救了我的一天。我已经尝试解决这个问题已经一个小时了。再次感谢你。 - Nishit Maheta
@ceejayoz,有其他的方法可以做到同样的效果吗? - Nishit Maheta
@NishitMaheta 我更新了我的回答。不确定这是否符合您的要求。 - Pedro M. Silva
@PedroM.Silva 是的,但我的类不在同一个命名空间中。 - Nishit Maheta
1
@NishitMaheta 那么你要么需要手动创建命名空间的映射(我在我的答案中提到的别名类型),要么找出一些命名空间的模式。如果不知道类的结构更多信息,很难建议解决方案。 :) - Pedro M. Silva

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接