using components in Cakephp 2+ Shell - components

I am trying to implement a task using the cakephp shell for my application. The task involves running a long running process (hence the need to use the shell).
The function requires me to use a function inside a Component called CommonComponent
Unfortunately whenever i try to include the component i get the following error
PHP Fatal error: Class 'Component' not found in /var/www/nginx-test/app/Controller/Component/CommonComponent.php
Here is the CronShell Class which is being called
class CronShell extends AppShell {
public function main() {
$this->out('Hello world.');
// $this->out(phpinfo());
}
public function test()
{
$this->out('Before Import');
App::import('Component', 'Common');
$this->out('Import complete');
// $this->Common=ClassRegistry::init('CommonComponent');
$this->Common =new CommonComponent();
$this->out('Initialization complete');
$this->Common->testCron();
$this->out('FunctionCall complete');
//$this->Common->saveCacheEntry("name","value");
}
}
The CommonComponent class is stored as app/Controller/Component/CommonComponent.php and is as follows
class CommonComponent extends Component
{
function testCron()
{
$this->out('Hello world from Component.');
}
}
Any ideas?

I had to do this recently with MTurk component I wrote. My final solution was using a lib instead of a component. Then I had the component access the lib so I could use the methods from both a component and from shell.
However, here is code that WILL allow you to load a component from a shell.
<?php
App::uses('AppShell', 'Console/Command');
App::uses('ComponentCollection', 'Controller');
App::uses('Controller', 'Controller');
App::uses('MTurkComponent', 'Controller/Component');
class ProcessCompletedTask extends Shell {
public function execute() {
$this->out("Processing...\n");
$collection = new ComponentCollection();
$this->MTurk = new MTurkComponent($collection);
$controller = new Controller();
$this->MTurk->initialize($controller);
$this->MTurk->processHITs();
$this->out("Complete\n");
}
}

What you import into the Shell should be code from within your Apps Lib
the component can also make use of the Lib code - but you'll not need to do a load of tedious stuff
if you set it up right you'll make you app cleaner
if you import the component you'll need to pass it a component collection and so you'd have to make that from witin shell not that your use it (or if you do you must be doing it wrong)

Have you tried App::uses('Component', 'Controller'); at the top of your file, ev. before import of CommonComponent? Then I guess you need to do what sam says, or you could use the $this->Controller->Components->load('CommonComponent') but for that you need to construct the Controller class.

I believe that it is semantically wrong to share functionality between controllers and shells.
If you require common functionality, it is clearer and neater to put it in a separate class, place this class in your vendors folder and then import the code into both the controller and the shell.
In addition, this approach does not prevent you from creating components and tasks that use the core functionality and then share these components and tasks between your controllers and shells.

Related

How can I access a repository globally in loopback 4?

I used loopback 4 to bootstrap my API application and developed some parts of it.
Now I'm trying to access repositories outside the controllers and tried everything I could but didn't help. None of my searches did too. For example I have a controller in which I can access repo such way.
constructor(
#repository(UserRepository) public userRepository: UserRepository){}
But if it isn't a controller it won't work and I found out I had a wrong understanding about #repository and #inject decorators.
My Case:
I want to run a cron-job to perform an update operation on database every day at a specific time.
The thing is I think I should create a service or something like that to expose database operations so it can be accessible anywhere.
The issue you're trying to tackle comes down to dependency injection. It's described at https://loopback.io/doc/en/lb4/Dependency-injection.html but that article does not explain how to create a separate class that works with dependency injection.
In short, in order to perform the dependency injection through decorators, LoopBack needs to be the one to construct the class you're writing, so that it can pick what you need from its Context. So, instead of writing new ClassSomething, it has to be #inject(ClassSomething) or #service(ClassSomething), and those decorators only work in a class also instantiated by LoopBack. In other words, your class has to be in the "call tree" from the original application class (application.ts) for dependency injection to work.
The easiest way to do what you want to do is to use LoopBack's Cron component: see https://loopback.io/doc/en/lb4/Running-cron-jobs.html. You could convert your class to a CronJob if it has no other purpose, or let the CronJob create an instance of your class. In the latter case, you need to bind your class to the application Context so that the CronJob can access it through decorators. You can do so in two ways:
Convert your class to a Service (see https://loopback.io/doc/en/lb4/Service.html and https://loopback.io/doc/en/lb4/Service-generator.html). If what your class does can be thought of as a service, this is the way to go.
Inject your class with: #service(MyServiceClass) myService: MyServiceClass
Directly bind your class to the application context, in application.ts:
this.bind('MyClass').toClass(MyClass);
Inject your class with: #inject(MyClass) myClass: MyClass
The latter option is not best practice AFAIU as it does not adhere to IoC-principles (https://en.wikipedia.org/wiki/Inversion_of_control) - basically, by hard-coding the class binding in the application class, it is not exactly modular. When your class is converted to a service (and placed in src/services), it will automatically be added to the Context, meaning you can inject it everywhere using #service().
I also experience the same(cron word). I couldn't find any solution still in the documentation. But still, I have achieved this in this way.
just call repository as like class with dependency in it. do this in your index.ts file where you start the application.
async function startCronJobs(app: SawLoopbackApplication) {
const userRepo = app.repository(UserRepository);
const userRepoInstance = await userRepo.getValue(app);
const cron = new CronComponent(userInstance);
//---- Exicute your cron here
new CronJob('0 6 * * *', function () {
cron.sendMorningMail()
}).start();
//---- Exicute your Second Cron
new CronJob('0 8 * * 1', function () {
cron.weeklyAppraisalsToAgent()
}).start();
}
call your cron in the functional component and execute a raw query to get the details from DB etc...
import {MysqlDataSource} from '../../datasources'
const query = `select * from accounts where IP="${ipAddress}" ORDER By
createdAt DESC Limit 1`
const dataSource = new MysqlDataSource()
const accountDetails = await dataSource.execute(query)
console.log(accountDetails)
instead of a raw query. call your repository from the function component below
import {MysqlDataSource} from '../datasources'
import {ContactUsUserDetailsRepository} from '../repositories'
const contactUsUserDetailsRepository = new ContactUsUserDetailsRepository(new MysqlDataSource)
function saveContactDetails(){
const payload={UID:"Unique ID",name:"Durai"}
await contactUsUserDetailsRepository.create(payload)
}
I would prefer 2 because, if you have a lot of imports in your cron class constractor you need to pass all of them while calling a function like in the 3rd point.
Have fun:)

posibilty to get service whithout moduleRef.get

I Can take instance of service using this example:
export class GetEntityDomainService {
constructor(private readonly moduleRef: ModuleRef) { }
getEntity(): void {
const myObject = this.moduleRef.get(MyClassName);
}
}
but how could I invoke a service instance outside of a class object
where I don't have a handle to moduleRef:
here is example from angular:
const injector = ReflectiveInjector.resolveAndCreate(providers);
const widgets: WidgetService = injector.get(WidgetService);
https://kevinphelps.me/blog/2017-01-17-using-angular-dependency-injection-in-node
whether is it possible to download an instance of the service without needing a moduleRef?
Thanks
Piotr
There are several reasons
sometimes I need to inject a service in a function, then I have to pass moduleRef to this function to make it possible
The second example is Command and Events -
for example I create such an action, then I pass parameters to send to handler,:
this.dispach(new Command (parameters ...))
Currently my actions know from which module they are triggered and in which module their handler is - so I won't send actions between wrong areas of the system (layers, dependencies). I check all this when executing
this.dispach (new Command ())
now I have to write such a dispatch as follows:
this.dispach (new Command (this.moduleRef, ... other parameters))
could I create a moduleRef from the code?, it would facilitate the process of creating an action and eliminate the need to pass moduleRef
but I don't know if it will help me
as I analyzed DI nest, it works differently from angular
The service knows where it was declared and when it injects something in it, it uses the tokens declared in this module
I suppose I will only be able to inject general scope

How to use default constructor in codeIgniter4?

I want to use controller to load directly some models by using default constructor, but CodeIgniter4 was removed __construct method from CodeIgniter4 framework and I got error message Cannot call constructor. please see code below:
public function __construct(){
parent::__construct();
//Do magic task here
}
Instead of CI3 is working by using above code, Can anyone suggest me in CI4?
Thank You for your tips or comments!
Best Regards!
CI4 default controller namespaced CodeIgniter\Controller doesn't have a class constructor. So if you're extending directly from it, parent::__construct() can't be called.
If you really need a constructor for every one of your controller you should modify App\Controllers\BaseController and making your others controllers extending it with the code you provided.
Also if your goal is to execute some code before or after your controller is called you should check out Filters in CI4. They are perfectly designed for this need : https://codeigniter4.github.io/userguide/incoming/filters.html
You can use constructor in CI4 to load models
Add the code in your Controller class and it should work.
protected $userModel;
public function __construct()
{
$this->userModel = new UserModel();//Create a instance of the model
helper('form', 'url');
}

How to run a cli script in pimcore

till Now I have been placing all my code within pimcore\cli\startup.php and it's run fine but when I'am trying in another way I getting error like
Class 'Website\ObjectExporter' not found in C:\xampp\htdocs\pimcore\website\var\cli\export-objects.php on line 7
How to run code in this way
For example create an ObjectExporter.php under /website/lib/Website folder with this content:
<?php
namespace Website;
class ObjectExporter
{
public function exportObjects()
{
// Your code
}
}
Then you can either instantiate this class in your controller action or in a CLI script. Controller actions are within /website/controllers folder and they need to be called through http: http://localhost?controller=default&action=default
Example: /website/controllers/DefaultController.php
<?php
class DefaultController extends Website_Controller_Action {
public function defaultAction () {
$this->disableViewAutoRender();
$objectExporter = new Website\ObjectExporter();
$objectExporter->exportObjects();
}
}
(You could also add your whole code directly into action, but that would be a bit ugly solution, it of course depends)
But better and quickest way to approach such tasks is with the CLI scripts.
I like to use the /website/var/cli folder (you need to create it manually, but the /website/var folder is excluded in .htaccess by default which makes it practical for such use cases).
Example: /website/var/cli/export-objects.php
<?php
$workingDirectory = getcwd();
chdir(__DIR__);
include_once("../../../pimcore/cli/startup.php");
chdir($workingDirectory);
$objectExporter = new Website\ObjectExporter();
$objectExporter->exportObjects();
Then just run it by issuing this command in your command line:
php website/var/cli/export-objects.php
This is the standard way of creating CLI scripts in pimcore:
https://www.pimcore.org/wiki/pages/viewpage.action?pageId=16854341
Basically it's just Symfony/Console, which is documented in here detail:
http://symfony.com/doc/current/components/console/introduction.html

Jmeter BSF using Groovy, Import one script's function to another

I use groovy in my Jmeter BSF, and sometimes I have functions that are used frequently enough to be moved to some script which I than can use as a library.
My approach was to create a file, say "library.groovy", and add there some function
public void function()
{
println("hello!");
}
and then use the following code in my BSF script
import library.groovy;
function();
Both files lie in the same dir, but script refuses to locate library. I also tried to explicitly wrap this function into class but I took no effect as well.
Can anyone suggest a solution for this?
Update:
I tried almost all possible solutions described in the internet. And everything that works in groovy console or Eclipse does not in Jmeter. Probably that is because of BSF. Anyone knows some workarounds?
I just had this problem, and solved it in a way that seems, to me, nicer-looking. It is basically winstaan74's answer, but with the extra bits needed to make it work.
You have your function's groovy file, named say: MyJmeterFunctions.groovy:
package My.JmeterFunctions
public class MyHelloClass {
public void hello() {
println("Hello!");
}
}
Then you compile this from the terminal:
$groovyc -d myJmeterFunctions myJmeterFunctions.groovy
and turn it into a .jar inside the /lib folder of your jmeter install, with all the other .jar files that came with jmeter
$jar cvf /<>/apache-jmeter-2.8/lib/myJmeterFunctions.jar -C myJmeterFunctions .
Now, restart jmeter. It won't know about your new .jar until you do.
Lastly you have the script that you want to run the hello() function from, which your jmeter BSF assertion/listener/ whatever points to:
import My.JmeterFunctions.*
def my_hello_class_instance = new MyHelloClass();
my_hello_class_instance.hello();
And this is what worked for me. If you'd rather organize you .jar into a different folder than jmeter's /lib, I believe you can run jmeter using (from here):
$jmeter -Jsearch_paths=/path/to/yourfunction.jar
But I haven't tried that myself.
I ended up having 2 files like below:
"MyHelloClass.groovy"
public class MyHelloClass {
public void hello() {
println("Hello!");
}
}
And "HelloScript.groovy"
try {
ClassLoader parent = getClass().getClassLoader();
GroovyClassLoader loader = new GroovyClassLoader(parent);
Class groovyClass = loader.parseClass(new File("../GroovyScripts/MyHelloClass.groovy"));
GroovyObject helloClass = (GroovyObject) groovyClass.newInstance();
helloClass.hello();
}
catch (Throwable e) {
println(e.toString());
}
Then I can run "HelloScript.groovy" in BSF from Jmeter.
I think you'll need to wrap your helper methods in a class, and then import that class. So, your helper methods file should contain..
package library
class UsefulFunctions {
static function() {
println 'hello'
}
}
And then in your test script, say
import static library.UsefulFunctions.*
function()
Now, this is just scratching the surface, but I hope it'd be enough to get you started.

Resources