通过App :: import在组件中使用CakePHP组件

时间:2011-06-14 18:00:17

标签: php cakephp factory-pattern

我正在使用CakePHP来构建一个应用程序,其中包括生成数十个图形。为了方便加载这些图形,我使用组件遵循工厂模式。有一个ChartComponent负责加载组件以生成单个图形,这些单独的图形可以依次使用组件来准备它们的数据。

示例GraphComponent:

class ChartComponent extends Object {
    var $name = 'Chart';

    function getChart($category, $chart, $node) {
        // Build the name we expect to find the component as.
        $component_name = Inflector::camelize("{$category}_{$chart}_graph");
        $component_class = "{$component_name}Component";

        // Import the component, making sure it actually exists.
        if (! App::import('Component', $component_name)) {
            return false;
        }

        $chart = new $component_class();
        return $chart->getData($node);
    }
}

单个图表的示例组件:

class StoreRevenueGraphComponent extends Object {
    var $name = 'StoreRevenueGraph';
    var $components = array('Calculations');

    function getData($node) {
        var_dump(isset($this->Calculations));
    }
}

但是,当我运行此代码时,Calcluations组件未成功加载,并且isset($ this-> Calculations)返回false。

我认为这是因为我错过了某些初始化代码。有没有人知道我必须采取什么其他步骤来通过App :: import在另一个组件中使用组件,或者是我试图在蛋糕内部做不到的事情?

解决方案

正如Andrew所指出的,当手动实例化组件时,var $ components数组永远不会被处理,导致它们永远不会加载。解决方案是手动执行此操作:

function startup(&$controller) {
    foreach ($this->components as $component_name) {
        App::import('Component', $component_name);
        $component_class = "{$component_name}Component";
        $this->$component_name = new $component_class();
        $this->$component_name->startup($this);
    }
}

5 个答案:

答案 0 :(得分:1)

$components属性只能在控制器中使用。如果您希望一个组件加载多个其他组件,您可以覆盖组件中的startup方法并创建组件并手动将其分配给->Calculations属性。

像这样:

function startup( $controller ) {
  $this->Calculations = new Foo();
  $this->Calculations->startup($component);
}

然而,这有点奇怪。大多数应用程序更有可能的是,您希望将其加载到控制器本身。所以代码变成了:

$component->Calculations = new Foo();
$component->Calculations->startup($component);

如果控制器没有直接使用该组件,最好将这些类放在vendors目录中并将它们用作外部库。

[编辑]

在这个答案之后看到评论。

答案 1 :(得分:1)

几乎......修正的startup()方法是

   function startup(&$controller) {
        foreach ($this->components as $component_name) 
        {
            App::import('Component', $component_name);
            $component_class = "{$component_name}Component";
            $this->$component_name = new $component_class();
            $this->$component_name->startup($this);
        }
    }

答案 2 :(得分:1)

在Cake 2.3中,原始帖子中给出的解决方案会产生警告错误,尽管它确实正常运行并且不返回false:

  

警告(4096):传递给Component :: startup()的参数1必须是   Controller的实例,给定的MessagesComponent实例,调用   ...(删除)... / MessagesComponent.php在第26行并定义   [CORE / cake-lib-2.3.1 / Controller / Component.php,第120行]

违规行是:

$this->$component_name->startup($this);

我将其更新为

$this->$component_name->startup($controller);

它消除了错误并正常运行。

2.3的完整工作版本是:

   function startup(&$controller) {
        foreach ($this->components as $component_name) 
        {
            App::import('Component', $component_name);
            $component_class = "{$component_name}Component";
            $this->$component_name = new $component_class();
            $this->$component_name->startup($controller);
        }
    }

附录:

使用较新版本的PHP(5.5.9)将操作系统升级到Ubuntu 14.04会产生新的警告消息(仍使用CakePHP 2.3.6,后续版本的Cake可能没有此问题):

  

严格(2048):MessagesComponent :: startup()的声明应与Component :: startup(Controller $ controller)兼容[APP / Controller / Component / MessagesComponent.php,第0行]

为了消除这种情况,必须改变启动声明:

function startup(&$controller) {

为:

function startup(Controller $controller) {

答案 3 :(得分:0)

关于cookbook,您可以像控制器一样加载其他组件。您可以在$ components var。

中声明它
// app/Controller/Component/CustomComponent.php
App::uses('Component', 'Controller');
class CustomComponent extends Component {
    // the other component your component uses
    public $components = array('Existing');

    public function initialize(Controller $controller) {
        $this->Existing->foo();
    }

    public function bar() {
        // ...
   }
}

// app/Controller/Component/ExistingComponent.php
App::uses('Component', 'Controller');
class ExistingComponent extends Component {

    public function foo() {
        // ...
    }
}

答案 4 :(得分:0)

你可以使用如下:)

class YourComponent extends Component {
  public function initialize(Controller $controller)
   {
     $this->controller = $controller;
  if (!isset($this->controller->presetVars)) {
        $this->controller->presetVars = true;
    }

    $model = $this->controller->modelClass;
    if (!empty($settings['model'])) {
        $model = $settings['model'];
    }

    if ($this->controller->presetVars === true) {
        // auto-set the presetVars based on search definitions in model
        $this->controller->presetVars = array();
        $filterArgs = array();
        if (!empty($this->controller->$model->filterArgs)) {
            $filterArgs = $this->controller->$model->filterArgs;
        }

        foreach ($filterArgs as $key => $arg) {
            if ($args = $this->_parseFromModel($arg, $key)) {
                $this->controller->presetVars[] = $args;
            }
        }
    }
    foreach ($this->controller->presetVars as $key => $field) {
        if ($field === true) {
            if (isset($this->controller->$model->filterArgs[$key])) {
                $field = $this->_parseFromModel($this->controller->$model->filterArgs[$key], $key);
            } else {
                $field = array('type' => 'value');
            }
        }
        if (!isset($field['field'])) {
            $field['field'] = $key;
        }
        $this->controller->presetVars[$key] = $field;

   }

  /* now you can use Component existing in your Component :) */
  public function sayHello(){
     $this->controller->Session->setFlash(__('Hello you'));
  }

}