I am trying to write an extension to my Controller class. The problem is I can't seem to figure out how..
I have the following class named test in which there is one function which simply returns aaaa and in in the same file, at the end, as my Controller:
class test extends Controller
{
function test()
{
parent::Controller();
}
function echoMe(){
return 'aaaaaaaaaaaaaaaaa';
}
}
Within my Controller class I have a function which is the general output for a clients homepage. I'm trying to call the function echoMe from the extension above, but I keep getting
Call to undefined method Controller::echoMe()
Here is the client function (the call to echoMe() is right at the top):
function controller_client1($controlData = NULL)
{
echo $this -> echoMe();
//as the client page is content from the xml, mmodel needs the page number
$pageNumber = '';
if(isset($_GET['number']))
{
$num = htmlentities($_GET['number']);
if(ctype_digit($num) && $num >= 0)
{
$pageNumber = $num;
}
}
else{
$pageNumber = 0;
}
//loading the page content
$data = $this->model->model_loadXMLdata($pageNumber);
if(!empty($controlData))
{
//check if there is any info in the control data sent
foreach($controlData as $key => $value)
{
//add the info to the data array
$data[$key] = $value;
}
}
$this->load->load_clientHomePage($data);
}
I know this is a very simple question. I've been trying to follow this guide, but something isn't clicking...
Could somebody please help? How can I call the function echoMe() from test?
I know how to write just a brand new class and call it, but I'm trying to learn how to extend properly and keep failing.
Am I meant to call the "test" from within the Controller somewhere?
In the config.php you set the prefix for the file you want to extend. So it should be My_test, unless you have changed this preset(displayed below)
/*
|--------------------------------------------------------------------------
| Class Extension Prefix
|--------------------------------------------------------------------------
|
| This item allows you to set the filename/classname prefix when extending
| native libraries. For more information please see the user guide:
|
| http://codeigniter.com/user_guide/general/core_classes.html
| http://codeigniter.com/user_guide/general/creating_libraries.html
|
*/
$config['subclass_prefix'] = 'MY_';
Make sure you have the controller in the right folder (application/core in codeigniter 2.1.0) then you shouldn't have a problem. Hope that helps
here is my controller that i extend. The file is called My_Controller.php (creative I know)
<?php
class MY_Controller extends CI_Controller {
function __construct()
{
parent::__construct();
//constructor code here
}
//Custom functions here
}
//sencond controller I extend in the same file
class MY_Admin extends CI_Controller {
function __construct()
{
//more custom stuff for admin stuff
}
//more custom functions for admin stuff
}
?>
Has you see I have two extended controllers in the same file.
My extending code looks like this:
<?php
class home extends MY_Controller
{
and then just replace My_Controller with My_Admin if I want to extend the admin.
echoMe() function is defined in test class and not in Controller class. When controller_client1 function is called with the instance of Controller class, echoMe does not exist because it is not defined within Controller class.
The best way to achieve this is to create empty echoMe function in base Controller class. This way polymorphism works. When calling controller_client1 function from instance of test class, method from that class will be executed. Otherwise, method from base class will be executed.
I hope I didn't miss the point of the question :)
echo $this -> echoMe(); will fail because its created in the child(extended) class and your calling it in the parent class. The question is a little hard to understand.
abstract class Controller{
public function __construct(){
}
public function echoMe($str){
echo $str;
}
}
class test extends Controller{
public function __construct(){
parent::echoMe('aaaaaaaaaa');
}
}
Try this (general idea, not CodeIgniter only):
$test = new test();
echo $test->echoMe();
Remember, test extends your Controller class, not the other way round - so you can't call your methods outside the test class/object instance.
Also, it might be a good idea to upgrade to a new version of CodeIgniter - beware of the new parent controller name, though.
EDIT
Ok, this should be enought to get you started - note that it's PHP5, not PHP4, so constructors are called __construct and not the class name:
class Controller {
public $mainvar;
function __construct() {
$this->mainvar = '<br />';
}
function echoMe() {
return 'aaaaaaaaaaaaaaaaa';
}
}
class Test extends Controller {
function __construct() {
parent::__construct();
}
function echoMeAgain(){
return 'bbb';
}
}
$test = new Test();
echo $test->echoMe();
echo $test->mainvar;
echo $test->echoMeAgain();
I think the question has actually already been answered by Kosta, but there might be some misunderstanding at your side. So let me extend that by some example code:
class Controller {
public function run() {
$this->echoMe();
}
}
class test extends Controller {
public function echoMe() {
echo "works";
}
}
// This does NOT work, because Controller::echoMe does not exist
$controller = new Controller();
$controller->run();
// This DOES work, because $this will be instance of test, and
// test::echoMe exists and is callable.
$test = new Test();
$test->run();
"extends" does not mean, that the actual Controller class gets extended. The new class "test" just inherits every single method and property that is not declared "private" from the Controller class. The Controller class itself remains untouched.
Related
I have to different classes in my Slim PHP framework, named OrderController & AddressController. I want to access some function of AddressController inside OrderController to reduce code redundancy.
But can't get a way to do it, I got how to do it in pure PHP setup, but how to do it in Slim PHP framework?
The PHP way to do this is as follows:
class A {
private $xxx;
public function __construct() {
$this->xxx = 'Hello';
}
public function getXXX() {
return $this->xxx;
}
}
class B {
private $a;
public function __construct(A $a) {
$this->a = $a;
}
function getXXXOfA() {
return $this->a->getXXX();
}
}
$a = new A();
$b = new B($a);
$b->getXXXOfA();
How to achieve this dependancy injection in Slim?
Slim PHP Framework
Note: I am using Slim PHP v3
2 solutions come into mind:
-1-
You could also try to have the common functionality in a separate Trait.
-2-
I won't do the
new SecondController($container)
inside the constructor of the FirstController unless you need it at every controller-hit.
I like lazy loading, so it will load only when needed.
If your AddressController and OrderController has same parent class, than move these methods to parent:
class AddressContoller extends Controller {
public function test() {
$this->methodFromParent();
}
}
If not, create new object of that class and call method. Method must be public
class AddressContoller extends Controller {
public function test() {
$order = new OrderController();
$order->publicMethodInOrderClass();
}
}
If your OrderController wants to call a method foo from AccessController, you should think about moving foo somewhere else. That's an good indicator for wrong SRP
There are two possibilities
foo belongs to/is relevant for every Controller and has something to do with controlling: Just move it to the parent class.
foo is relevant to only a few classes: Move it to the class, it belongs to. This could be an helper class, some domain model class, or something else. Maybe you have to intruduce a new class to do this.
After a lot of reseach I finally manage to get a solution! Posting it here so if anyone in future might get help from it:
class FirstController
{
protected $container;
protected $db;
protected $view;
protected $second;
// constructor receives container instance
public function __construct(\Interop\Container\ContainerInterface $container) {
$this->second = new SecondController($container);
$this->container = $container;
$this->db = $this->container->db;
$this->view = $this->container->view;
}
public function LocalFunction(){
$this->second->otherFunction();
//call the functions in other classes as above
}
}
What exactly means every part in string $this->load->model in codeigniter? In the code below "$this" makes reference to the User class(controller)? Also, does it invoke the method load that invokes model? Or does $this make reference to the name of every function where is put?
<?php
class User extends CI_Controller {
function __construct()
{
parent::__construct();
$this->template->set_layout('adminLayout.php');
$this->load->model("User_model");
$this->load->Model('Auth_model');
}
function index()
{
$this->Auth_model->isLoggedIn();
$this->template->title('Admin ::');
$this->template->build('admin/index');
}
?>
$this refers to the class you are in.
$this isn't something from CodeIgniter, but from PHP. $this refers to the current object.
models are build usually to handle all database relations so basically the line
$this->load->model("User_model");
Says "Load the model "name of model" so we can use it.
Whenever you create a class
$something = new SomeClass();
Then $this refers to the instance that is created from SomeClass, in this case $something. Whenever you are in the class itself, you can use $this to refer to this instance. So:
class SomeClass {
public $stuff = 'Some stuff';
public function doStuff()
{
$this->stuff;
}
}
$this refers to global CodeIgniter object. If you var_dump($this) in constructor or in called method, you will see all invoked and initialized code. You can track changes in way that you could load some library, helper, method, language, package or config or anything else allowed by Loader.php class of framework. You will get similar output to outputting get_instance() function.
That contstruction uses load() method of Config.php core system file you could check with start on line 119. Model in line means type of file need to be loaded.
Basically it is refered to load method of Config class that act on needed Loader class methods (model, helper etc).
That means you are loading models(User_model and Auth_model) so you can use the functions which are inside those models.
For example: if you have Auth_model as follow
<?php
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Auth_model extends CI_Model {
function __construct()
{
// Call the Model constructor
parent::__construct();
}
function insert_valid_val($valid_data)
{
//Write queries here and return some values.
}
Then in controller you can invoke the insert_valid_val() as follow.
<?php
class User extends CI_Controller {
function __construct()
{
parent::__construct();
$this->template->set_layout('adminLayout.php');
$this->load->model("User_model");
$this->load->Model('Auth_model');
}
function index()
{
$this->Auth_model->isLoggedIn();
$this->template->title('Admin ::');
$this->template->build('admin/index');
$returned_val = $this->Auth_model->insert_valid_val("send some array");
print_r($retunred_val);
}
?>
I have a class base which has a property called load which is a object of the class load. The load class has a function called view that includes pages. Now I need to call,
This is similar to CodeIgniter's $this->load->view("test.php");
Load Class
class Load {
public function view($page){
//this function loads views to display
include($page);
}
public function model($class){
//loads model classes
}
public function library($class){
//loads libraries
}
}
Base Class
class Base {
function __construct(){
$this->load = new Load();
}
}
Index page
$base = new Base();
$base->load->view("test1.php");
this1.php
echo "testing1\n";
$this->load->view("test2.php");
test2.php
echo "testing2";
The output should be
testing1
testing2
What you really want I think is to follow a factory pattern. (At least, that's what you mean if you want the $view variable to actually contain an instance of the Load class)
Make the constructor protected, so that the only the class can create new instances, then in the base class add a static method, e.g. 'factory' which returns an instance of the desired class.
Then your code would look like
$view=Base::factory();
$view->view("test1.php");
NOTE: this answer was made before any edit made to the question. Please evaluate accordingly
You need to have the functions marked as public to allow them to be called from outside of the defining class (this is simplified of course)
Try the following:
class Load{
public function view($page){
include($page);
}
}
class Base{
public $load;
function __construct(){
$this->load = new Load();
}
}
(The uppercase class names are my own preference)
This should work, but it's not a good design from a clean OOP perspective, because the users of the Base class need to know how the Load class works. This is called "tight coupling" and should be avoided as much as possible.
I suggest to consider the following alternative:
class Load{
public function view($page){
include($page);
}
}
class Base{
private $load; //note the private modifier
function __construct(){
$this->load = new Load();
}
public function view($page){
$this->load->view($page);
}
}
This way I just need to know that Base has a method view($page) and i don't have to know anymore what Load does at all.
If in the future you want to change the Load class you can do it under the hood without the Base users ever noticing it, if you do it right:
Suppose you define a class:
class BetterLoad {
private function foo(){
//do something awesome
}
private function advancedView($page){
include($page);
$this->foo();
}
}
and you want to incorporate this inside Base instead of the old Load.
class Base{
private $adv_load; //note the private modifier
function __construct(){
$this->adv_load = new BetterLoad();
}
public function view($page){
$this->adv_load->advancedView($page);
}
}
That's it. You won't need to change anything else in your code. Just go on using the old $base_obj->view($page) and you're good to go, without even noticing the change.
what i'm trying to do is call each method "init" from current class's parents.
I'm doing that to avoid programmers to have to call init method (parent::init()) each time they create an init method in a new controller.
Example:
class Aspic\Controller { } // main controller
class ControllerA extends Aspic\Controller { public function init() {/* do something 1 */}
class ControllerB extends ControllerA {}
class ControllerC extends ControllerB { public function init() { /* do something 2 */ }
class ControllerD extends ControllerC {}
As you can see the init methods do not call parent init method but i want my app (there is an option) do it.
Thus when I'm loading ControllerD, before calling it's init method (there isn't in the example but the app test it), i want to call each parent init method.
sound like this:
parent::init(); // Controller C init
parent::parent::parent::init(); // Controller A init
So i did :
if($this->_autoCallParentsInit) {
// Aspic\Controller is the main controller, which is the mother of all others
$aspicControllerRc = new \ReflectionClass('Aspic\\Controller');
$rc = new \ReflectionClass($this); // We are in D
$currPrefix = '';
// Calling each init methods of current class parent
// Avoid using parent::init() in each controller
while(($parentClass = $rc->getParentClass()) AND $aspicControllerRc->isInstance($parentClass)) {
/*
$aspicControllerRc->isInstance($parentClass)
=> because Aspic\Controller extends a "Base class". Thus, we stopped at Aspic\Controller
*/
$currPrefix .= 'parent::';
// Must have explicit method (not inherited from parent) BUT actually hasMethod does not care
if($parentClass->hasMethod('init')) {
call_user_func($currPrefix.'init');
}
}
}
This is not working because ReflectionClass::isInstance does not accept others argument than the object we want to test (and the not a ReflectionClass object representing it as in the example)
**
Simply:
I have an object $x, and i want to call the init method of each parent of the class of $x.
**
Is it possible ?
I hope i was clear :)
Thanks
ControllerB has an init() method by virtue of extending ControllerA, so you shouldn't have to call parent::parent::init() to get to A's from C. You should be fine to call parent::init() from ControllerD, which will call ControllerC's init() method. If ControllerC calls parent::init() it will be calling ControllerA's init() method.
If you're trying to skip the Controller's specific init() code when being called by a subclass, you could add a flag function init($call_parent = false) and then, from lower controllers, call parent::init(true);
If you're not using the classes statically (which, from your code not stating static function, I assume you're not), have you tried using the __construct() method? It gets automatically called when you instantiate the class, for example:
class MyClass {
public function __construct() {
echo 'Hello!';
}
}
$class = new MyClass();
That will automatically output 'Hello!', however if you extend the class and that child class contains a __construct() method you will have to put parent::__construct() inside the childs construct method, however you wont have to do it for every parent, just the once, for example:
class MyClassB extends MyClass {
public function __construct() {
parent::__construct();
echo 'World!';
}
}
class MyOtherClass extends MyClassB
public function __construct() {
parent::__construct();
echo 'How\'s it going!';
}
}
$class = new MyOtherClass();
That will output "Hello! World! How's it going!"
My problem is in using Codeigniter custom library but I think it is not specific to that and more related to use of constructors in PHP.
I am trying to create a custom controller library in Codeigniter like this...
class MY_Controller extends Controller {
var $data = array();
function MY_Controller() {
parent::Controller();
$this->data['err'] = 'no';
$this->load->helper('utilities');
}
}
Now I create a codeigniter controller class like this...
class api_controller extends MY_Controller {
function get_data() {
$this->data['view'] = "data";
$this->load->view("data_view", $this->data);
}
function get_xml() {
$this->data['part'] = "xml";
$this->load->view("data_view", $this->data);
}
}
I want to ask that if the controller class extending the MY_Controller is instantiated when I access urls like api_controller/get_data and api_controller/get_xml, does the constructor of parent class always get called upon, i.e., MY_Controller() is always called.
Am I correct in inferring the following
get_data() called
-> $data => array ('err' => 'no', 'view' => 'data')
get_xml() called
-> $data => array ('err' => 'no', 'part' => 'xml')
Your code looks like it's using the PHP4 syntax for constructors. You should switch to the PHP5 syntax.
PHP4:
class MyClassName {
function MyClassName() {...} //constructor.
}
PHP5:
class MyClassName {
function __construct() {...} //constructor.
}
You can then call the constructor of a parent class by calling parent::__constructor(); from within the child class's __constructor() method:
class MyClassName extends SomeOtherClass {
function __construct() {
//...code here runs before the parent constructor.
parent::__construct();
//...code here runs after the parent constructor.
}
}
For PHP in general the parent constructor is not called default if the child has a constructor.
Constructors. It can be called using parent::_construct();
If you're using php 5+ you should go with the new recommended style of using function __construct() instead of the old style with a function named the same as a class.
As for CI-specific stuff I can't help you, sorry!
If you do not override __construct() in MY_Controller then Controller's __construct() will get run.
If you override it and then called parent::__construct() then it will run your own and the parent's.
So if you wanted it to run the parent's and your own you would do this
class MY_Controller extends Controller
{
var $data = array();
function __construct()
{
parent::__construct();
// Your code here
}
}
Yes, the parent constructor it is always called (you may want to rewrite them as __construct() however, thinking also at codeigniter 2.0 ).
If you really are in doubt toss in it an echo and see it for yourself.
Also the $this->data part is correct to me
You are right in your affirmations about the data array contents. In you code you wrote:
function MY_Controller() {
parent::Controller();
so the parents's class constructor is being called. There are lots of comments about PHP4 and PHP5 syntax, but basically everithing is ok.
In your question you wrote
that if the controller class extending the MY_Controller is instantiated
that is not correct. The instance is an object of class api_controller, calling the MY_Controller constructor is made using the same object. That is not the same, that is basic for polimorphism.
class Blog extends CI_Controller {
public function __construct()
{
parent::__construct();
// Your own constructor code
}
}