我正在使用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);
}
}
答案 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'));
}
}