In CakePHP, how do I load a component in a shell?
To use a component in a controller, you include an array of components in a property called $components. That doesn
I have some xml utilities that I use across some of my controllers. One of the controller launches a heavy task via the cake console, so that it can quietly run in the background via PHP CLI, while the user's request is immediately completed (once the task done, it will e-mail the results to the user).
The xml utilities are generic enough to be used in controller and shell, are too specific to the application to warrant them vendor status. The offered solution with the Lib folder does not work as in CakePhp v3
there seems to be no Lib
folder.
After some quite some time spent, I managed to load my controller component to the shell task. Here is how to:
namespace App\Shell;
use Cake\Console\Shell;
use Cake\Core\App;
use Cake\Controller\Component;
use Cake\Controller\ComponentRegistry;
use App\Controller\Component\XmlUtilitiesComponent; // <- resides in your app's src/Controller/Component folder
class XmlCheckShell extends Shell
{
public function initialize() {
$this->Utilities = new XmlUtilitiesComponent(new ComponentRegistry());
}
...
$this->Utilities
can now be used across my entire shell class.
//TestShell.php
class TestShell extends AppShell{
public function test(){
//to load a component in dis function
App::import('Component', 'CsvImporter');
$CsvImporter = new CsvImporterComponent();
$data = $CsvImporter->get();
}
}
//CsvImporterComponent.php
App::uses('Component', 'Controller');
class CsvImporterComponent extends Component {
function get(){
//your code
}
}
I assume that you have a component named YourComponent:
<?php
App::uses('Component', 'Controller');
class YourComponent extends Component {
public function testMe() {
return 'success';
}
}
App::uses('ComponentCollection', 'Controller');
App::uses('YourComponent', 'Controller/Component');
class YourShell extends AppShell {
public function startup() {
$collection = new ComponentCollection();
$this->yourComponent = $collection->load('Your');
}
public function main() {
$this->yourComponent->testMe();
}
}
<?php
namespace App\Shell;
use App\Controller\Component\YourComponent;
use Cake\Console\Shell;
use Cake\Controller\ComponentRegistry;
class YourShell extends Shell {
public function initialize() {
parent::initialize();
$this->yourComponent = new YourComponent(new ComponentRegistry(), []);
}
public function main() {
$pages = $this->yourComponent->testMe();
}
}
If you think you have to load a component in shell your application architecture is bad designed and should be refactored.
Technically it is possible but it doesn't make sense and can have pretty nasty side effects. Components are not made to run outside of the scope of a request. A component is thought to run within the scope of a HTTP request and a controller - which is obviously not present in a shell.
Why does XML manipulation stuff have to go into a component? This is simply the wrong place. This should go into a class, maybe App\Utility\XmlUtils
for example and have no dependencies at all to the request nor controller.
The logic is properly decoupled then and can be used in other places that need it. Also if you get incoming XML the right place to do this manipulation (by using your utility class) would be inside the model layer, not the controller.
Because you've gone just against both principles.
You could have tried to search via Google or on SO you would have found one of these:
Be aware that some of them might encourage bad practice. I haven't checked them all.
If you are trying to access a custom XyzComponent from a shell, then you probably have commonly-useful functionality there. The right place for commonly-useful functionality (that is also accessible from shells) is in /Lib/
.
You can just move your old XyzComponent class from /Controller/Component/XyzComponent.php
to /Lib/Xyz/Xyz.php
. (You should rename your class to remove the "Component" suffix, e.g., "XyzComponent" becomes "Xyz".)
To access the new location, in your controller, remove 'Xyz' from your class::$components
array. At the top of your controller file, add
App::uses('Xyz', 'Xyz'); // that's ('ClassName', 'folder_under_/Lib/')
Now you just need to instantiate the class. In your method you can do $this->Xyz = new Xyz();
Now you're using the same code, but it can also be accessed from your Shell.