Integrating CLI PHP with CakePHP - php

I have a nice functioning CakePHP 1.3.11 site and I need a scheduled maintenance CLI script to run, so I'm writing it in PHP. Is there any way to make a cake-friendly script? Ideally I could use Cake's functions and Cake's Database models, the CLI requires database access and not much else however. I would ideally like to include my CLI code in a controller and the datasource in a model so I can call the function like any other Cake function, but only from the CLI as a sheduled task.
Searching for CakePHP CLI mostly brings results about CakeBake and cron jobs; this article sounded very helpful but it's for an old version of cake and requires a modified version of index.php. I'm no longer sure how to change the file to make it work in the new version of cakePHP.
I'm on Windows if it matters, but I have complete access to the server. I'm currently planning to schedule a simple cmd "php run.php" style script.

Using CakePHP's shells, you should be able to access all of your CakePHP app's models and controllers.
As an example, I've set up a simple model, controller and shell script:
/app/models/post.php
<?php
class Post extends AppModel {
var $useTable = false;
}
?>
/app/controllers/posts_controller.php
<?php
class PostsController extends AppController {
var $name = 'Posts';
var $components = array('Security');
function index() {
return 'Index action';
}
}
?>
/app/vendors/shells/post.php
<?php
App::import('Component', 'Email'); // Import EmailComponent to make it available
App::import('Core', 'Controller'); // Import Controller class to base our App's controllers off of
App::import('Controller', 'Posts'); // Import PostsController to make it available
App::import('Sanitize'); // Import Sanitize class to make it available
class PostShell extends Shell {
var $uses = array('Post'); // Load Post model for access as $this->Post
function startup() {
$this->Email = new EmailComponent(); // Create EmailComponent object
$this->Posts = new PostsController(); // Create PostsController object
$this->Posts->constructClasses(); // Set up PostsController
$this->Posts->Security->initialize(&$this->Posts); // Initialize component that's attached to PostsController. This is needed if you want to call PostsController actions that use this component
}
function main() {
$this->out($this->Email->delivery); // Should echo 'mail' on the command line
$this->out(Sanitize::html('<p>Hello</p>')); // Should echo <p>Hello</p> on the command line
$this->out($this->Posts->index()); // Should echo 'Index action' on the command line
var_dump(is_object($this->Posts->Security)); // Should echo 'true'
}
}
?>
The whole shell script is there to demonstrate that you can have access to:
Components that you load directly and that are not loaded through a controller
Controllers (first import the Controller class, then import your own controller)
Components that are used by controllers (After creating a new controller, run the constructClasses() method and then the particular component's initialize() method as shown above.
Core utility classes, like the Sanitize class shown above.
Models (just include in your shell's $uses property).
Your shell can have a startup method that is always run first, and the main method, which is your shell scripts main process and which is run after the startup.
To run this script, you would enter /path/to/cake/core/console/cake post on your command line (might have to check the proper way to do this on Windows, the info is in the CakePHP book (http://book.cakephp.org).
The result of the above script should be:
mail
<p>Hello</p>
Index action
bool(true)
This works for me, but maybe people who are more advanced in CakePHP shells could offer more advice, or possibly correct some of the above... However, I hope this is enough to get you started.

As of CakePHP 2, the shell scripts should now be saved to \Console\Command. There is good documentation at http://book.cakephp.org/2.0/en/console-and-shells.html

Related

Using Yii1.x model function in external PHP controller outside Yii framework

I am working on a Yii1 old website. which is linked with some external PHP controllers. These external controllers provide some common functions that are used between 2 different applications. I have a function in Yii model that I want to use in one of the external PHP controller is there a way to do this? Currently, this is done by rewriting MySQL query in the PHP external controller but I don't want to follow this lame practice.
I found this link and I am able to access Yii externally but it's still not very helpful. Using Yii in 3rd-Party Systems
Here's a sample of my code:
namespace main\Helpers;
require_once('path/to/yii.php');
Class HelperClass {
public static function yiisupport($id){
// I am able to access Yii variables using
\Yii::app()->name
// But how to access the yii model or controller functions? I need something like the follwoing
$model = \Yii::app()->YiiModel::model()->findByPK($id);
}
}
Can anyone help?
You need to create Yii application first (using config file path) to access its models and controllers as it is mentioned in the documentation. Then you can access any model class in your external application just like you would access it in your Yii application, and you can use controller actions as below;
$controller = new \YOURController('ACTION_NAME');
$controller->ACTION_NAME();
If you have already imported models/controllers in your config file then you will not need to import any class but if you have not then you can import specific model/controller like below;
\Yii::import('application.models.MODEL_NAME');
\Yii::import('application.controllers.CONTROLLER_NAME');
Check the examples below;
namespace main\Helpers;
require_once('path/to/yii.php');
\Yii::createWebApplication('path/to/config.php');
Class HelperClass {
public static function yiisupport($id){
// Access Yii variables
\Yii::app()->name;
// Access yii model
$model = \YiiModel::model()->findByPK($id);
// Access yii controller and its actions
$controller = new \YiiController('actionCreate');
$controller->actionCreate();
}
}
Update:
As mentioned by #rob006 in the comment below that calling yii controller action outside Yii application is a bad idea, however if you still want to do that, there is a safer way which follows the Yii application lifecycle and this way access filters and beforeAction() will be triggered. So you can call controller action in a safer way as below;
\Yii::app()->runController('route/to/action');

Accessing other model file within another model file

I am trying to create an class inside the model directory. This class(eg:- Admin) exposes only the methods which makes sense to the controller.
The Admin class will do all the joins and stuffs internally on tables(using ORM) and prepares data which can be readily consumed by the controller.
I have created 15 files in the model directory each of them representing a table in my database using the ORM method.
Now I want to create to create an instance of table within the Admin classes' get_All() method. I have tried to use Kohana::factory() which was unavailable in my Admin class. I tried to create the instance using the 'new', but it ended in an error which says that the specified class is not found.
My class definition for Admin is as follows
<?php defined('SYSPATH') or die('No direct script access.');
class Model_Admin {
public function get_All()
{
$PD = new Model_PayPalData;
echo 'Success';
}
}
The error is:
ErrorException [ Fatal Error ]: Class 'Model_PayPalData' not found
APPPATH/classes/Model/admin.php
Please advice on how to deal with this situation.
Thanks for your attention
Seem like your class is not loaded. You can check this by viewing the declared classes
get_declared_classes();
If it is't there, make sure to include it, or add it to the Models directory, if needed without extending the ORM class.

PHPUnit and Selenium - run tests from another class

I am using PHPUnit and Selenium to test my web application.
At the moment I have 2 test classes - UserTest and PermissionsTest.
In UserTest I have methods which test that the program can successfully create a new user.
In PermissionsTest I turn certain permissions on and off and test the outcome.
For instance, I might turn the 'Create User' permission off and then test that the 'Create User' button is disabled. However, if I turn the 'Create User' permission back on, I want to test that it is possible to create a user.
All of the logic for being able to create a user is already in the UserTest class - so is there any way of running tests from the UserTest class from the PermissionsTest class?
At the moment I am trying the following code:
public function testUserPermission(){
$userTest = new UserTest();
if($this->hasPermission = true){
$userTest->testCanCreateUser();
}
}
However when I run this test, I get the error "There is currently no active session to execute the 'execute' command. You're probably trying to set some option in setup() with an incorrect setter name..."
Thanks!
It sounds to me like you're missing separation of your test implementation with its logic - I'm not talking about PHP issue but general test model.It will allow you to reuse your test components in various test cases.
You can take a look on some
material about Page Objects in PHP here or general selenium wiki.
The solution was as follows:
//instantiate and set up other test class
$userTest = new UserTest();
$userTest->setUpSessionStrategy($this::$browsers[0]);
$userTest->prepareSession();
//carry out a test from this class
$userTest->testCanCreateUser();
This works nicely. I can't see why using functionality from another test class is a bad idea in this case, because if I didn't do that I'd have to just rewrite that functionality into my new class, which seems less 'pure'...
For Selenium 1 (RC),
I made the following modifications instead (as well as applying the Page Object design pattern):
Specific Test class
//instantiate and set up other test class
$userTest = new UserTest($this->getSessionId());
//carry out a test from this class
$userTest->createUser();
//asserts as normal
$userTest->assertTextPresent();
...
Base Page Object class
class PageObject extends PHPUnit_Extensions_SeleniumTestCase {
public function __construct($session_id) {
parent::__construct();
$this->setSessionId($session_id);
$this->setBrowserUrl(BASE_URL);
}
}
Specific Page Object class
class UserTest extends PageObject {
public function createUser() {
// Page action
}
}

Using Yiic to access an action

I'm trying to use yiic to run an action that's inside a controller (protected/controllers/site.php)
class SiteController extends Controller {
public function actionHello() {
echo 'hello!';
}
}
If I try to run (inside protected/ folder) ./yiic site hello
It says it only has the commands message, migrate, shell and webapp.
How do I call this action within the Command Line ?
Short answer. You can't :-) You need to create Yii an override of CConsoleCommand (more info on the Yii guide here).
Once you've done that, then you create an action (or shift your code right over to that action).

How do I extend the code igniter controller class?

In my CI system\libraries directory I have a new class named DD_Controller.php. This file looks like this:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class DD_Controller extends Controller
{
protected $ddauthentication;
function __construct()
{
parent::Controller();
$this->ddauthentication = "Authenticated";
}
}
?>
My application controller is defined like this:
class Inquiry extends DD_Controller
{...}
The Inquiry class works fine when I extend Controller, but I get a
Fatal error: Class 'DD_Controller' not
found in
C:\development\localhost\applications\inquiry\controllers\inquiry.php
on line 4
When I extend DD_Controller. In the config file I have the prefix defined as such:
$config['subclass_prefix'] = 'DD_';
Any idea of what I'm missing?
TIA
This is a better approach. Do the following:
Go to the following directory: your_ci_app/application/core/ and create a php file called MY_Controller.php (this file will be where your top parent classes will reside)
Open this the file you just created and add your multiple classes, like so:
class Admin_Parent extends CI_Controller {
public function __construct() {
parent::__construct();
}
public function test() {
var_dump("from Admin_Parent");
}
}
class User_Parent extends CI_Controller {
public function __construct() {
parent::__construct();
}
public function test(){
var_dump("from User_Parent");
}
}
Create your children controllers under this directory your_ci_app/application/controllers/ . I will call it adminchild.php
Open adminchild.php and create your controller code, make sure to extend the name of the parent class, like so:
class Adminchild extends Admin_Parent {
function __construct() {
parent::__construct();
}
function test() {
parent::test();
}
}
DD_Controller.php should be in /system/application/libraries/
If you're using the same CI for multiple apps, and you want them all to be able to extends their controllers to your custom one then you can extend the base Controller class in the same file.
In system/libraries/Controller.php below the Controller class:
class Mega_Controller extends Controller {
function Mega_Controller()
{
parent::Controller();
// anything you want to do in every controller, ye shall perform here.
}
}
Then you'll be able to do this in your app controllers:
class Home extends Mega_Controller {
....
Since the extended controller class you created will be available. I think this is better then overwriting the base controller, but that would work as well.
I recommend to avoid "cracking" CodeIgniter core files.
Better use its native extending possibilities and try to fit into them.
The same rule I would recommend for any PHP library / CMS.
This rule has few reasons:
- ability to quiclky upgrade without takint into account thousands of notes where and how was cracked in core files;
- portability;
- possibility to share your code - eg, this will be usable by both you and your friends in case of need, and it will help them to keep their library up to date, the same as you.
In other words, this is much more professional and it pays to you in the future by usability, portability and by update application possibility.
Regarding your personal question...
As for me, there is nothing bad to create your own library with everything you need to extend native CodeIgniter Controller, then load this library in Controller's constructor and you are done. The only thing to make better usability is to give short name to your library.
This way you can even divide what you need in different pieces and put into separate libraries:
WebFeatures
AdminFeatures
etc.
Then you just load needed libraries in your controller's constructor and you are done.
P.S. I know that proposed way does not fit into "right" OOP concept, but in the same time you must never forget about the integrity of the libraries used.
Everything above is just one more view of mine 7-years experience in professional web development, so I hope it will be helpful if not to follow, then at least to take into account.
Regards,
Anton

Categories