I have got this controller:
class Start extends CI_Controller{
var $base;
var $css;
function Start()
{
parent::Controller(); //error here.
$this->base = $this->config->item('base_url'); //error here
$this->css = $this->config->item('css');
}
function hello($name)
{
$data['css'] = $this->css;
$data['base'] = $this->base;
$data['mytitle'] = 'Welcome to this site';
$data['mytext'] = "Hello, $name, now we're getting dynamic!";
$this->load->view('testView', $data);
}
}
it tells me in this line:
parent::Controller(); //error here.
Call to undefined method CI_Controller::Controller()
If I remove that line..I get an error for the next line that says..
Call to a member function item() on a non-object
How do I prevent such errors form happening?
If you're using CI 2.x then your class constructor should look like this:
public function __construct()
{
parent::__construct();
// Your own constructor code
}
read more in user guide
In CodeIgniter 2, the constructor is named __constructor and not the class name. So you need to call parent::__construct() instead of parent::Controller()
Here's an article that you can read that shows one major difference between CodeIgniter 1.x and CodeIgniter 2.x
http://ulyssesonline.com/2011/03/01/differences-between-codeigniter-1-7-2-and-2-0-0/
If you're running your Codeigniter project via Xampp or a similar server add the following code to the bottom of your config.php file in the following directory; ci_project/application/config/config.php
function my_load($class) {
if (strpos($class, 'CI_') !== 0) {
if (is_readable(APPPATH . 'core' . DIRECTORY_SEPARATOR . $class . '.php' )) {
require_once (APPPATH . 'core' . DIRECTORY_SEPARATOR . $class . '.php');
}
}
}
spl_autoload_register('my_load');
The above code would help to; load class in core folder.
I'm certain this works in the following setup; CI-3+, Xampp, Php5.6, and or 5.6+
Also, you can then decide to create and allow other classes to reference your own Controller (which extends the original CI_Controller) by creating a file named MY_Controller.php in the following directory: ci_project/application/core/ and adding the following code in it;
<?php
class MY_Controller extends CI_Controller {
}
?>
That way you can always just reference or extend the other Classes to your own Controller (MY_Controller) throughout the rest of the project e.g.
class Admin extends MY_Controller {
//your function here
}
I hope this helps.
Related
My question is how to dynamically include and call the model and view classes in a simple MVC model? I have no problem calling the controller, I have found various ways to do that, but I can't find a good solution for calling and passing in the model and view.
I have setup a .htaccess file to read the url as "www.domain.com/controller/method/id".
I was previously trying to do a check if a file exists for the model and view the same way I am doing the controller using the $cont variable, and then trying to load the model and pass it into the controller, then the view. The issue I had is that all the includes are using the $cont variable to call instantiate their classes and could not tell each other apart. I tried adding a suffic $cont . 'Controller', but then I couldn't load the class at all, let alone pass in the model or view.
Here is my latest example without model or view.
<?php
//===============================================
// Debug
//===============================================
ini_set('display_errors','On');
error_reporting(E_ALL);
//===============================================
// Includes
//===============================================
require('coremvc.php');
//===============================================
// Constants & Globals
//===============================================
define('BASE_PATH', dirname(realpath(__FILE__)));
$GLOBALS['var'] = "";
//===============================================
// Session
//===============================================
session_start();
//===============================================
// Router
//===============================================
if ($_SERVER['REQUEST_URI'] !== '/') {
$uri = $_SERVER['REQUEST_URI'];
$uri = ltrim($uri, '/');
$request = explode('/', $uri);
foreach ($request as $key => $val) {
if(empty($request[$key])) {
unset($request[$key]);
}
}
$request = array_values($request);
if (isset($request[0])) {
$cont = $request[0];
}
if (isset($request[1])) {
$action = $request[1];
}
} else {
$cont = "home";
}
if (FILE_EXISTS('/controllers/' . $cont . 'Controller.php')) {
require '/controllers/' . $cont . 'Controller.php';
} else {
$cont = "home";
require '/controllers/homeController.php';
}
//===============================================
// Start the controller
//===============================================
$controller = new $cont;
I have made the following changes to the example above, posted it below, as well as my super easy bake oven simple controller.
<?php
if (FILE_EXISTS('/controllers/' . $cont . 'Controller.php')) {
require '/controllers/' . $cont . 'Controller.php';
} else {
$cont = "home";
$cont = ucfirst($cont) . 'Controller';
require '/controllers/homeController.php';
}
//===============================================
// Start the controller
//===============================================
$controller = new $cont('World');
$controller->world();
Controller, it is just extending an empty class which I was thinking I could use if I wanted to extend a common method to every class. That is what the coremvc.php is in the index.php above.
<?php
Class HomeController extends Controller
{
function __construct($world) {
echo "Hello ";
$this->world = $world;
}
function world() {
echo $this->world;
}
}
You want to load and call classes easily. I dynamically load classes that end in ".class.php". This makes things easy for me.
I put this in my index.php... where /app/ is where I have my php classes:
<?php
define('CLASS_DIR', '/app/');
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . CLASS_DIR);
spl_autoload_extensions('.class.php');
spl_autoload_register();
Next, I require my routes:
require 'rte/myroute.php';
I let my routes (controllers) direct my traffic to my models, albeit some parsing, etc.
I typically develop REST based APIs in PHP, so the "view" is just a JSON response.
The HTML / JavaScript client consumes the responses.
A good framework that I like is SlimPHP. I load it up using Composer.
http://www.slimframework.com/
https://getcomposer.org/
Here's an example of calling a class as an instance and statically, since you auto-loaded, you don't need to include anything at the top:
<?php
$param1 = 1;
$param2 = 2;
MyClass::myFunc($param1);
$myObj = new MyClass;
$myObj->myFunc2($param2);
As it is already say you can have a look to use the spl_autoload_register which will require all your files in the given path. You can change this function to improve the load.
Concerning the Model with your current code you can implement it as follow:
$controllerPath = "/controllers/{$cont}Controller.php";
$modelPath = "/model/{$cont}Model.php";
if (FILE_EXISTS($controllerPath)) {
require $controllerPath;
if (FILE_EXISTS($modelPath)) {
require $modelPath;
}
else {
throw new \LogicException(
sprintf("Your controller must implement a model. No model found: %s", $modelPath)
);
}
} else {
$cont = "home";
require "/controllers/{$cont}Controller.php";
require "/model/{$cont}Model.php";
}
//===============================================
// Start the controller
//===============================================
$controller = new $cont($cont);
In this sample of code, $cont is the name of the page like home. Which require the homeController and homeModel. Then in your __construct($modelName) just set the model.
However, I don't recommand you tu use this because your controller can load many Models. Then, your controller could look like this:
<?php
namespace \App\Controller; // If you use namespace
use App\Model\homeModel, // If you use namespace
App\Model\productModel; // If you use namespace
Class HomeController extends Controller
{
private $model;
/* If this function is common to all controllers just move it
in the parent Controller class( one more time, I don't recommend to set
the model using this way). */
public function __construct($model) {
$this->model= $model;
}
public function login() {
$homeModel = new homeModel(); // If you use namespace
// Example to call the view
$myValue = 'Hello world';
$this->render(array("myVariableName" => $myValue))
}
}
In this second example $this->render can be a method in your Controller class (which by the way should be an abstract class). I'll give a last sample of code for the logic of the abstract controller.
<?php
namespace \App\Controller;
abstract class AbstractController {
/* Common code for all controllers */
public function __construct() {
}
/* View renderer */
protected function render($parameters) {
/* Call the view here maybe an another class which manage the View*/
}
}
To conclude, you can implement this MVC in many way, this code is just a suggestion and maybe its not the best way. I advise you to have a look with the spl_autoload that I put at the beginning of my answer.
I am writing php codeigniter from scratch
and i am trying know write_file function in file helper
this is my controller
<?php
class Files extends CI_Controller {
var $file;
function Files()
{
$this->file = "application" . DIRECTORY_SEPARATOR . "files" . DIRECTORY_SEPARATOR . "hello.txt";
}
function write_file(){
$data = "Hello World !! :)";
write_file($this->file,$data ,'a');
echo "writing is finishing...";
}
}
and my version is 2.1.4, befog I put 'a' it works great but after I add it
it gives me this error
Call to undefined function write_file() in C:\wamp\www\test7\application\controllers\files.php
If you want to use a helper function in codeigniter, you'll need to load it first:
$this->load->helper('file');
Just add this line either in the your controller's constructor function or before you use write_file()
i'm updating my code and trying to use spl_autoload_register but IT SIMPLY DOESN'T WORK!!!
I'm using PHP 5.3.8 - Apache 2.22 on Centos / Ubuntu / Win7 and trying to echo something but i get nothing instead... have been trying to make it work for the last 3 hours with no result... this is driving me mad!!!
class ApplicationInit {
// Constructor
public function __construct() {
spl_autoload_register(array($this, 'classesAutoloader'));
echo 'construct working...!';
}
// Autoloading methods
public function classesAutoloader($class) {
include 'library/' . $class . '.php';
echo 'autoload working...!';
}
}
first echo from __construct works but the "classesAutoloader" doesn't work at all, this class is defined in a php file within a folder and i'm calling it from index.php like follows:
define('DS', DIRECTORY_SEPARATOR);
define('ROOT', getcwd() . DS);
define('APP', ROOT . 'application' . DS);
// Initializing application
require(APP.'appInit.php');
$classAuto = new ApplicationInit();
any help is truly appreciated, thanks in advance!
It seems like you're doing the wrong thing. The function that you pass to spl_autoload_register is what's responsible for loading the class file.
Your code is calling
$classAuto = new ApplicationInit();
but by that time, ApplicationInit is already loaded so the autoload function doesn't get called
A more logical way would be for you to to call
spl_autoload_register(function($class){
include 'library/' . $class . '.php';
});
Then when you call
$something = new MyClass();
And the MyClass is not defined, then it will call your function to load that file and define the class.
What is your problem? Your code is working correctly.
class ApplicationInit {
public function __construct() {
spl_autoload_register(array($this, 'classesAutoloader'));
echo 'construct working...!';
}
public function classesAutoloader($class) {
include 'library/' . $class . '.php';
echo 'autoload working...!';
}
}
$classAuto = new ApplicationInit(); //class already loaded so dont run autoload
$newclass = new testClass(); //class not loaded so run autoload
For my current project i decided to create a library for some common functionalities.
Ex : Login_check,get_current_user etc.
With my little knowledge i created a simple one but unfortunately its not working.
Here my library :
FileName : Pro.php and located in application/libraries
class Pro{
public function __construct()
{
parent::_construct();
$CI =& get_instance();
$CI->load->helper('url');
$CI->load->library('session');
$CI->load->database();
}
function show_hello_world()
{
$text = "Hello World";
return $text;
}
}
?>
And i tried to load it on my controller :
<?php
class Admin extends CI_Controller
{
function __construct()
{
parent::__construct();
$this->load->database();
$this->load->library(array('session'));
$this->load->library("Pro");
}
function index()
{
echo($this->Pro->show_hello_world());
}
}
?>
I cant see any erros there...but i am getting a blank page.
Whats wrong with me ??
Thank you .
Edit : I got this error :
Call to a member function show_hello_world() on a non-object in C:\wamp\www\Project\application\controllers\admin.php on line 13
One thing I notice: remove the parent::__construct() from your library constructor, because it's not extending anything so has no parent to call.
Also, enable error reporting by setting the environment to "development" in index.php, and you might also want to raise the logging threshold to 4 in config/config.php so you log errors.
Try this simple test-case:
file Pro.php in application/libraries:
class Pro {
function show_hello_world()
{
return 'Hello World';
}
}
Controller admin.php in application/controllers
class Admin extends CI_Controller
{
function index()
{
$this->load->library('pro');
echo $this->pro->show_hello_world();
}
}
while your class name is capitalized, all your references to the library when loading it and using it should be lower case. you also do not need the constructor, as the other commenter mentioned.
so instead of:
echo($this->Pro->show_hello_world());
you should have:
echo($this->pro->show_hello_world());
I prefer the standard php autoloader approach, with this you dont need to change your classes at all, you can use your standard classes without modifications
say for instance you class is class 'Custom_Example_Example2' and is stored in libraries
in sub folders you can add this autoloader in the master index.php
make sure it is added below the defined APPPATH constant
//autoload custom classes
function __autoload($className) {
if (strlen(strstr($className, 'Custom_')) > 0 ||
strlen(strstr($className, 'Other1_')) > 0 ||
strlen(strstr($className, 'Other2_')) > 0) {
$exp = explode('_', $className);
$file = APPPATH.'libraries';
if(!empty($exp)) {
foreach($exp as $segment) {
$file .= '/'.strtolower($segment);
}
}
$file .= '.php';
require_once $file;
//debug
//echo $file.'<br />';
}
}
This will look for class calls matching the 'Custom_' prefix
and reroute them to the relative location in this case
you only need to define the base prefix not the sub folders / classes
these will be auto detected by this code
APPPATH.'libraries/custom/example/example2.php'
You can call it in the controller the standard php way
$class = new Custom_Example_Example2;
or
$class = new custom_example_example2();
You can modify the script to your liking currently it expects all folders and filenames in the library to be lowercase but you can remove the strtolower() function to allow multiple casing.
you can change the require once to echo to test the output by uncommenting this line and refresh the page, make sure you have a class init / test in the controller or model to run the test
echo $file.'<br />';
Thanks
Daniel
In Pro.php
class Pro{
protected $CI;
public function __construct() {
$this->CI = & get_instance();
}
public function showHelloWorld(){
return "Hello World";
}
}
In your controller
class Staff extends CI_Controller {
public function __construct() {
parent::__construct();
$this->load->database();
$this->load->helper(array('url_helper', 'url'));
$this->load->library("pro");
}
public function index() {
echo $this->pro->showHelloWorld();die;
}
}
Just do these things you can access your custom library in codeignitor.
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.