From a code igniter function I am loading a library with arguments and it performs a bunch of things and initiliazes the library instance.
What is the right way to pass this instance to another function?
EDIT:
public function init()
{
//...
$dataArr = array('directory' => $myDir, 'run' => TRUE);
$this->load->library('lib_mylib', $dataArr);
// here I want to pass the lib_mylib instance to final();
// lib my lib has variables that gets set when loaded, I don't want to reloaded it again in the next function because it performs some operations that should only happen once. How can I get a handle of that initiliazed library inside final?
}
public function final($inputLibMyLib)
{
}
Assuming this code is in your Controller, just load the library in the Controller's constructor...
class Example extends CI_Controller {
// load your Controller wide library here
public function __construct()
{
parent::__construct();
$dataArr = array('directory' => $myDir, 'run' => TRUE);
$this->load->library('lib_mylib', $dataArr);
}
public funciton init()
{
$this->lib_mylib->function(); // is available
}
public funciton final()
{
$this->lib_mylib->function(); // is also available
}
}
This constructor is called every time a function is requested.
CLARIFICATION
Doing this in the constructor is the best place because it guarantees that it is loaded before any following functions are.
However...
If you are calling functions sequentially the loaded library will hold over...
example
class Example extends CI_Controller {
// load your Controller wide library here
public function __construct()
{
parent::__construct()
}
public funciton init()
{
$dataArr = array('directory' => $myDir, 'run' => TRUE);
$this->load->library('lib_mylib', $dataArr);
$this->lib_mylib->function(); // is available
// call next function
$this->final();
}
public funciton final()
{
// is also available if final() is called from within init()
$this->lib_mylib->function();
}
}
Just make sure to pass the library instance by reference to your function, and it should work.
Related
Im using phalcon 2.0.0 and i am trying to call a function with in another function but from the same class like shown below, for some reason i get a blank page. And when i comment the calling of 2nd function from first, the page loads properly.
<?php
use Phalcon\Mvc\User\Component;
class Testhelper extends Component {
public function f1($data) {
$tmp = $this->f2($data);
return $tmp;
}
public function f2($data) {
return '5'; // just testing
}
}
And btw im accessing the f1 function by the volt function extender like this
$compiler->addFunction('customfunc', function($resolvedArgs, $exprArgs) {
return 'Testhelper ::f1('.$resolvedArgs.')';
});
if someone could help me, it would be deeply appreciated.
Thanks guys
You are trying to call TestHelper f1() statically in Volt, where your class does not expose that function as a static.
You can change your code like this:
<?php
use Phalcon\Mvc\User\Component;
class Testhelper extends Component
{
public static function f1($data)
{
$tmp = self::f2($data);
return $tmp;
}
public static function f2($data)
{
return '5'; // just testing
}
}
and your Volt function will work. However you have to bare in mind that because you are calling things statically you won't have immediate access to all the di container services that the Component offers like so:
$this->session
$this->db
You will need to modify your code to pick the di container using the getDefault()
Another option is to use the code as you have right now, but register the TestHelper in your di container like so:
$di->set(
'test_helper',
function () {
return new TestHelper();
}
);
and then your volt function will need to change to:
$compiler->addFunction(
'customfunc',
function ($resolvedArgs, $exprArgs) {
return '$this->test_helper->f1('.$resolvedArgs.')';
}
);
Basically everything works if I hard code the URL in my Ajax_Controller, but I want it to access the URL from the CMS field I created.
Thanks in advance. (please ignore when I don't close my braces - just trying to copy / paste efficiently)
In /mysite/_config.php I created a custom config:
Object::add_extension('SiteConfig', 'CustomSiteConfig');
In /mysite/code/CustomSiteConfig.php I added a field where I'll store a URL:
class CustomSiteConfig extends DataObjectDecorator {
function extraStatics() {
return array(
'db' => array(
'COJsonPath' => 'Text'
)
);
}
public function updateCMSFields(FieldSet &$fields) {
$fields->addFieldToTab("Root.CO", new TextField("COJsonPath", "CO JSON URL"));
}
public function getCOJsonPath(){
return $SiteConfig.COJsonPath;
}
This successfully creates a tab in the main parent in the CMS called "CO" and a field named "CO JSON URL". I logged into my CMS and saved http://api.localhost/mymethod/ to that field.
Now I have created an Ajax page type to facilitate running Ajax commands without letting the web site user find where my APIs are, and because jQuery Ajax no likey XSS (cross site scripting).
In /mysite/code/Ajax.php:
class Ajax extends Page {
static $db = array(
);
static $has_one = array(
);
function getCMSFields()
{
$fields = parent::getCMSFields();
return $fields;
}
}
class Ajax_Controller extends Page_Controller {
public function getCO()
{
$buffer = self::createHttpRequest("http://api.localhost/mymethod/");
//$buffer = self::createHttpRequest($CustomSiteConfig::getCOJsonPath());
return $buffer;
}
This code works, but when I try to execute my createHttpRequest() with the line you see commented out, it fails. I know my syntax is wrong, I just can't figure out what it should be. Thanks for helping - I've done this before I just can't figure it out - its Friday.
I spotted several syntax errors in your code:
public function getCOJsonPath(){
return $SiteConfig.COJsonPath;
}
should be:
public function getCOJsonPath(){
return $this->owner->COJsonPath;
}
1) $SiteConfig is never defined at that point.
2) usually you would use $this, but in your case you are inside a DataObjectDecorator, so you have to use $this->owner
3) you can not use . to access properties of an object, in php you have to use ->
moving on to class Ajax_Controller, inside getCO there are the following errors:
1) $CustomSiteConfig is not defined, therefore can not be used
2) getCOJsonPath is not a static function, but you try to call it as static (again you have to use ->
so, the code should look something like this:
public function getCO() {
$siteConfig = SiteConfig::current_site_config();
$buffer = self::createHttpRequest($siteConfig->getCOJsonPath());
return $buffer;
}
that should work, but there is another think that could be improved.
As I understand it, you are creating an ajax page, which you then create once in the CMS and tell your website content authors never to touch the ajax page?
This is quiet ugly, and there are several nice ways to do what you want to do.
Here is how I would create an Ajax controller:
_config.php
// tell SilverStripe what URL your AjaxController should have,
// here we set it to AjaxController::$URLSegment which is 'myAjaxController'
// so the url to the controller is mysite.com/myAjaxController
Director::addRules(100, array(
AjaxController::$URLSegment => 'AjaxController',
));
AjaxController.php
<?php
class EventAssetsController extends Controller {
public static $URLSegment = 'myAjaxController';
// tell SilverStripe what URL should call what function (action)
// for example, mysite.com/myAjaxController/foo should call the function foo
public static $url_handlers = array(
'foo' => 'foo',
'bar/$ID/$OtherID' => 'bar',
'co' => 'getCO'
);
public function Link($action = null) {
// this function is just a helper, in case you evern need $this->Link()
return Controller::join_links(self::$URLSegment, $action);
}
public function AbsoluteLink($action = null) {
return Director::absoluteURL($this->Link($action));
}
public function foo(SS_HTTPRequest $request) {
// do something here
// this method is an action, the url to this action is:
// mysite.com/myAjaxController/foo
}
public function bar(SS_HTTPRequest $request) {
// do something here
// this method is an action, the url to this action is:
// mysite.com/myAjaxController/bar
// you notice that the $url_handlers has "bar/$ID/$OtherID",
// that means you cann call mysite.com/myAjaxController/bar/21/42
// and silverstripe will make 21 the ID, and 42 the OtherID
// you can access ID and OtherID like this:
// $ID = $request->param('ID'); // which is 21
// $OtherID = $request->param('OtherID'); // which is 42
}
public function getCO() {
// this method is an action, the url to this action is:
// mysite.com/myAjaxController/co
$siteConfig = SiteConfig::current_site_config();
$buffer = self::createHttpRequest($siteConfig->getCOJsonPath());
return $buffer;
}
}
I am trying to initialize data in index function of controller, so that initialized data can be used in subsequent functions of controller. But the problem is data is not being displayed when I am trying to access it from other function. All of this is just to follow a sort of object oriented pattern.
Here is my code.
class Dashboard extends CI_Controller
{
private $account_data; /*Declaration*/
private $profile_data;
function __construct() {
// code...
}
function index() /*Here I am initializing data*/
{
$this->load->model('db_model');
$this->account_data = $this->db_model->get_row();
$this->profile_data = $this->db_model->get_row();
$this->load->view('user/dashboard');
}
function function account_details()
{
print_r($this->account_data); // This displays nothing
}
/*other function...*/
}
Idea is to get data once and use it for other functions and if data is updated again calls a function to initialize it.
But it is not working out. Please help me. Also suggest if I am following right approach.
Thanks for your time.
index method is not initializer, its default page/sub_method,
if you call the "*account_details*" in url as index.php/dashboard/account_details the index wont be called.
try put the code on constructor,
class Dashboard extends CI_Controller
{
private $account_data; /*Declaration*/
private $profile_data;
function __construct() { /*Here I am initializing data*/
parent::CI_Controller(); // Thank you Sven
$this->load->model('db_model');
$this->account_data = $this->db_model->get_row();
$this->profile_data = $this->db_model->get_row();
}
function index()
{
$this->load->view('user/dashboard');
}
function function account_details()
{
print_r($this->account_data); // This displays nothing
}
/*other function...*/
}
Note : don't the models or other computations on __construct() if you don't need on all methods of this controller.
create a private method like "model_initializer()" put this codes on this scope, and the call it in your other methos as $this->model_initialize(); if you need.
Thanks yo Sesama Sesame for note,
I have a problem, everything was working but then I tried to put my functions in my own libraries (to use them in different controllers) and it doesn't work.
I have SIGNUP controller with this:
$this->load->library('Check_functions');
// We check the form
$return_verif_form_signup = $this->check_functions->verif_form_signup($language);
which calls my librarie Check_functions:
class Check_functions {
public function verif_form_signup($language) {
if ($this->input->post()){
// Verification rules
$this->form_validation->set_rules('name', 'lang:name', 'trim|required|xss_clean');
....
if ($this->form_validation->run($this)) {
extract($_POST);
...
...
}
But I get the error:
Fatal error: Call to a member function post() on a non-object
Does anyone know how I could fix it?
thanks!
EDIT:
I have found the problem, the callback function is not called. If I replace callback_free_email by REQUIRED and I don't enter an email, my form is not submitted, so it's okay.
But if I have the following code, my form is always submitted. So the callback function is never called...
This is my code (i'm using HMVC):
class Check_functions {
private $CI;
public function __construct(){
$this->CI =& get_instance();
}
public function verif_form_signup($language) {
if ($this->CI->input->post()){
$this->CI->form_validation->set_rules('name', 'lang:field_name', 'trim|required|min_length[3]|max_length[25]|xss_clean');
$this->CI->form_validation->set_rules('email_signup', 'lang:field_email', 'callback_free_email');
...//other rules
if ($this->CI->form_validation->run($this->CI)) {
.....
}
}
}
public function free_email($str) {
return FALSE; // I have temporarly set that so I see if my function is called
}
}
I have a file called MY_Form_validation.php as suggested here: http://codeigniter.com/forums/viewthread/143057/#769347
class MY_Form_validation extends CI_Form_validation{
function run($module = '', $group = ''){
(is_object($module)) AND $this->CI = &$module;
return parent::run($group);
}
}
I really don't know what's wrong... why my callback function is not called?
Thank you for your help!
when you are writing libraries, you have to manually grab the Codeigniter instance like this
$CI =& get_instance();
then you would use $CI where you would normally use $this to interact with loaded codeigniter resources
so...
instead of
$this->input->post();
you would write
$CI->input->post();
Docs explain it here http://codeigniter.com/user_guide/general/creating_libraries.html
EXAMPLE LIBRARY STRUCTURE
class Examplelib {
// declare your CI instance class-wide private
private $CI;
public function __construct()
{
// get the CI instance and store it class wide
$this->CI =& get_instance();
}
public function lib_function()
{
// use it here
$this->CI->db->etc()
}
public function another_func()
{
// and here
$this->CI->input->post();
}
}
I finally found a workaround, instead of using the callback in my rule, I do a test later and call a verification function.
It’s works well like that.
Thanks for your help!
I have a PHP web application built with CodeIgniter MVC framework. I wish to test various controller classes. I'm using Toast for unit testing. My controllers have no state, everything they process is either saved into session or passed to view to display. Creating a mock session object and testing whether that works properly is straightforward (just create a mock object and inject it with $controller->session = $mock).
What I don't know, is how to work with views. In CodeIgniter, views are loaded as:
$this->load->view($view_name, $vars, $return);
Since I don't want to alter CI code, I though I could create a mock Loader and replace the original. And here lies the problem, I cannot find a way to derive a new class from CI_Loader.
If I don't include the system/libraries/Loader.php file, the class CI_Loader is undefined and I cannot inherit from it:
class Loader_mock extends CI_Loader
If I do include the file (using require_once), I get the error:
Cannot redeclare class CI_Loader
Looks like CI code itself does not use require_once from whatever reason.
Does anyone here have experience with unit testing CodeIgniter powered applications?
Edit: I tried to inject a real loader object at run-time into a mock class, and redirect all calls and variables with __call, __set, __get, __isset and __unset. But, it does not seem to work (I don't get any errors though, just no output, i.e. blank page from Toast). Here's the code:
class Loader_mock
{
public $real_loader;
public $varijable = array();
public function Loader_mock($real)
{
$this->real_loader = $real;
}
public function __call($name, $arguments)
{
return $this->real_loader->$name($arguments);
}
public function __set($name, $value)
{
return $this->real_loader->$name = $value;
}
public function __isset($name)
{
return isset($this->real_loader->$name);
}
public function __unset($name)
{
unset($this->loader->$name);
}
public function __get($name)
{
return $this->real_loader->$name;
}
public function view($view, $vars = array(), $return = FALSE)
{
$varijable = $vars;
}
}
Alternatively, you could do this:
$CI =& get_instance();
$CI = load_class('Loader');
class MockLoader extends CI_Loader
{
function __construct()
{
parent::__construct();
}
}
Then in your controller do $this->load = new MockLoader().
My current solution is to alter the CodeIgniter code to use require_once instead of require. Here's the patch I'm going to send to CI developers in case someone needs to do the same until they accept it:
diff --git a/system/codeigniter/Common.php b/system/codeigniter/Common.php
--- a/system/codeigniter/Common.php
+++ b/system/codeigniter/Common.php
## -100,20 +100,20 ## function &load_class($class, $instantiate = TRUE)
// folder we'll load the native class from the system/libraries folder.
if (file_exists(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT))
{
- require(BASEPATH.'libraries/'.$class.EXT);
- require(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT);
+ require_once(BASEPATH.'libraries/'.$class.EXT);
+ require_once(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT);
$is_subclass = TRUE;
}
else
{
if (file_exists(APPPATH.'libraries/'.$class.EXT))
{
- require(APPPATH.'libraries/'.$class.EXT);
+ require_once(APPPATH.'libraries/'.$class.EXT);
$is_subclass = FALSE;
}
else
{
- require(BASEPATH.'libraries/'.$class.EXT);
+ require_once(BASEPATH.'libraries/'.$class.EXT);
$is_subclass = FALSE;
}
}
I can't help you much with the testing, but I can help you extend the CI library.
You can create your own MY_Loader class inside /application/libraries/MY_Loader.php.
<?php
class MY_Loader extends CI_Loader {
function view($view, $vars = array(), $return = FALSE) {
echo 'My custom code goes here';
}
}
CodeIgniter will see this automatically. Just put in the functions you want to replace in the original library. Everything else will use the original.
For more info check out the CI manual page for creating core system classes.
I'm impressed by the code you are trying to use.
So now I'm wondering how the 'Hooks' class of CodeIgniter could be of any help to your problem?
http://codeigniter.com/user_guide/general/hooks.html
Kind regards,
Rein Groot
The controller should not contain domain logic, so unit tests make no sense here.
Instead I would test the controllers and views with acceptance tests.