I have a controller "MY_Controller.php" in "application/core" that extends "CI_Controller":
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class MY_Controller extends CI_Controller
{
function __construct()
{
parent::__construct();
$this->load->helper('form');
$this->load->helper('url');
$this->load->helper('security');
$this->load->helper('language');
// Load language file
$this->lang->load('en_admin', 'english');
}
}
I created another controller called "Auth.php" in "application/controllers" that extends "MY_Controller":
class Auth extends MY_Controller
{
function __construct()
{
parent::__construct();
$this->load->library('ion_auth');
if ($this->ion_auth->logged_in() === FALSE) {
redirect('user/login');
}
}
}
I created a third controller "Dashboard.php" that should extend "Auth", but throws an error:
class Dashboard extends Auth
{
public function index()
{
echo 'Hello from the dashboard';
}
}
Fatal error: Class 'Auth' not found in /home/user/www/forum/application/controllers/Dashboard.php on line 5
Would appreciate your advice to solve this problem.
The link in my comment has multiple ways to solve your problem. I prefer to use the #3 method - "Using an autoload function with hooks". Here's what works for me.
application/config/config.php
$config['enable_hooks'] = TRUE;
application/config/hooks.php
$hook['pre_system'][] = array(
'class' => '',
'function' => 'register_autoloader',
'filename' => 'Auto_load.php',
'filepath' => 'hooks'
);
application/hooks/Auto_load.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
function register_autoloader()
{
spl_autoload_register('site_autoloader');
}
/*
* Custom autoloader.
* This piece of code will allow controllers and other classes
* that do not start with "CI_" to be loaded when
* extending controllers, models, and libraries.
*/
function site_autoloader($class)
{
if(strpos($class, 'CI_') !== 0)
{
if(file_exists($file = APPPATH.'core/'.$class.'.php'))
{
require_once $file;
}
elseif(file_exists($file = APPPATH.'libraries/'.$class.'.php'))
{
require_once $file;
}
elseif(file_exists($file = APPPATH.'models/'.$class.'.php'))
{
require_once $file;
}
}
}
Your first extend of CI_Controller - class MY_Controller extends CI_Controller - should probably be in application/core/MY_Controller.php. Any classes that extend MY_Controller should be in application/libraries/.
The linked page labels the "hook" method as "the slow and right way" but the profiling I did comparing it to "2. Using an autoload function (the fast and dirty way)" showed a time difference of slightly over a microsecond. That's not worth worrying about.
I used the "fast and dirty way" for many years but kept forgetting about it when upgrading CI and I'd overwrite the config file resulting in a lost autoloader routine. Don't have that problem using the hook method other than remembering to set $config['enable_hooks'] to TRUE.
This is the regular behavior. Controllers do not extend each other. They may only extend MY_Controller (or CI_Controller). This is true for all MVC frameworks.
The way to use ion_auth, is not to extend Auth controller. You have already loaded ion_auth library, so you may directly use its methods in all controllers, the same way you use them in Auth controller.
Documentation for ion_auth here.
Well I've never used MY_Controller. I specify my class names.
I create all my classes under application/libraries and my structure is
Admin extends Common_Controller
Common_Controller handles the auth stuff for admin and members login/auth etc
Admin is a Controller up in application/modules/admin/controllers/Admin.php
Common_Controller extends Base_Controller
Base_Controller handles all the stuff common to all controllers like the templating etc
Base_Controller extends MX_Controller
MX_Controller is the wiredesignz HMVC Controller
MX_Controller extends CI_Controller
and we are done.
This is of course a HMVC setup but the principle is the same. Just don't use MY_Controller, give it a real name and use it normally.
And of course HMVC uses MY_Loader and other classes MY_xxx it uses to hook itself into CI and they live under application/core and it all plays nicely.
So as far as I can tell, there isn't a limit on how many classes deep you can go when you are extending them.
Related
This question already has answers here:
Codeigniter extending extended MY_Controller
(5 answers)
Closed 8 years ago.
class Settings extends USER_Controller {...}
class USER_Controller extends MY_Controller {...}//limit access to user and define some params and functions that is user depended
class MY_Controller extends CI_Controller {...}//provide extra functionality accross all controllers
well if i try to create above 3 class's this will not work.
yet if i just use settings extends MY_Controller it will work fine.
so is there a way i can put a middle class between my controller and MY_Controller -the base controller that extends CI_Controller ?
As #Minhaz Ahmed said, the problem is loading the class. Codeigniter only loads the class with the subclass prefix as in,
https://github.com/EllisLab/CodeIgniter/blob/develop/system/core/CodeIgniter.php#L276
if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
{
require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
}
So after some diggings I have came with a solution using both codeigniter hooks and spl_​autoload_​register() for my project without hacking the CORE. Please follow the steps to achieve what's in THE QUESTION.
Enable hooks in the config file if it isn't already.
create a hook in application/config/hooks.php as below.
$hook['pre_system'] = array(
'class' => '',
'function' => 'autoload',
'filename' => 'autoload.php',
'filepath' => 'hooks',
'params' => ''
);
Create the autoload.php in application/hooks folder.
inside the autoload.php file,
function autoload()
{
spl_autoload_register(function($class)
{
if(strpos($class,'CI_') !== 0 && file_exists(APPPATH.'core/'.$class.EXT))
{
require_once APPPATH . 'core/' . $class . EXT;
}
});
}
That's it.
Note: Here, I've used pre_system hook, not pre_controller, since codeigniter loads the base controller classes between pre_system and pre_controller hooks.
Hope this helps. If there are any issues with this please do comments.
Place the user_controller class file at the end of MY_Controller.php
MY_Controller.php in /application/core/
class MY_Controller extends CI_Controller {...}
class USER_Controller extends MY_Controller {...}
Now from your controller in your controllers folder, you can extend the controller from USER_Controller:
class Settings extends USER_Controller {...}
What did the best job for CI 2.1.4:
application/core
create for example:
- base_controller (base instructions, extending from ci_controller)
- admin_controller (extending from basecontroller and adds authentication instructions)
Then use them in: application/controllers:
for example:
class users extends admin_controller...
You could use Phil's core solution, its simple and easy.
http://philsturgeon.co.uk/blog/2010/02/CodeIgniter-Base-Classes-Keeping-it-DRY
You can extend the class the way you want but the problem is loading the class, you have to include your USER_Controller your Settings Class. Problem is not extend problem is including the file, so you have to find a way to include the second class.
See http://www.php.net/manual/en/function.spl-autoload.php
My codeigniter site autoloads sessions. I have an XML API page that I created but I'm getting a session error because of that autoload. I would prefer not to load sessions on this controller but I don't want to have to load sessions manually on all of my other controllers. Can that be done?
Use a base controller to load the session class rather than autoload.php and have your controllers extend it. More information here: http://ellislab.com/codeigniter/user-guide/general/core_classes.html
// application/core/MY_Controller.php
class MY_Controller extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->library('session');
}
}
// you may add additional base controller classes here
You must extend this controller with the ones that you want to have access to the session class, so unfortunately you will have to make some edits to your existing controllers:
class UserController extends MY_Controller {
public function index()
{
// session class is loaded
}
}
Other controllers can continue to extend CI_Controller and the session class won't be loaded.
I use this method for all my CI projects and rarely use the autoloader.php, it allows much more flexibility.
All I'm trying to do is something fairly simple :
Create a class (let's say brandNewClass - NOT MY_Controller) which extends CI_Controller
Create other controllers which extend brandNewClass
E.g.
class brandNewClass extends CI_Controller {
public function index()
{
}
public function info()
{
}
}
used like (in a file under /controllers) :
<?php
class newController extends brandNewClass
{
}
?>
The thing is, although it works when I'm copying the file under /application/core and naming it as MY_Controller, when I change the name to something more... self-explanatory, it doesn't.
Fatal error: Class 'brandNewClass' not found in .... on line ..
I've even tried using the __autoload function mentioned here, but without any luck.
Any ideas?
Have a look at this excellent tutorial - I hope it helps
http://codeigniter.tv/a-10/Extending-the-core-MY_Controller-and-beyond
The autoloader doesn't automaticly include other controllers. you will have to include it manually like this:
if (!defined('BASEPATH'))exit('No direct script access allowed');
include_once(APPPATH . 'controllers/brandnewclass.php');
If you want to create a custom base controller and make other controllers extend there, you can do it in following ways:
Create MY_Controller extending CI_Controller in application/core/ folder and make other controllers extend MY_Controller as MY_Controller will be autoloaded from core (This I guess you already know but want other alternatives.
Create MY_Controller in application/core/. Then create other level of Controllers that can primarily be Admin_Controller and Frontend_Controller. Now, one of these controllers will make base for your actual used controllers.
e.g. in application/core/MY_Controller.php
class MY_Controller extends CI_Controller {
public function __construct(){
parent::__construct();
}
}
Then Admin and Frontend controllers will be created in application/libraries/ and will extend MY_Controller like
class Admin_Controller extends MY_Controller {
public function __construct(){
parent::__construct();
}
}
Now, Any controller can extend one of these 2 controllers but for doing that you will have to autoload them. For autoloading in this case there can be a confusion because setting autoload['libraries'] in config/autoload.php will not work . That autoload works inside a controller but here we need to autoload before that i.e. in the class declaration. Will need to set this code in config/config.php
function __autoload($class)
{
$path = array('libraries');
if(strpos($class, 'CI_') !== 0) {
foreach($path as $dir) {
$file = APPPATH.$dir.'/'.strtolower($class).'.php';
if (file_exists($file) && is_file($file))
#include_once($file);
}
}
}
Now you can create your own controller
class newController extends Admin_Controller
{
}
This is the most suggested method making your structure quite clean and effective. May take some effort in understanding for the first time but is definitely worth it.
Third method is just a tweak of the second one, just based on the condition you mentioned of not using MY_Controller
You can make Admin_Controller or Frontend_Controller extend CI_Controller directly and not extend MY_Controller
That may just lead to some duplicity of code in both these controllers if that may be the case
http://philsturgeon.co.uk/blog/2010/02/CodeIgniter-base-Classes-Keeping-it-DRY
I suspect you're trying something similar?
There's an autoload function that you can add to the config file so that you needen't require_once() the class all the time.
You should declare the class as abstract, since it shouldn't be instantiated directly.
You'll need to modify the CodeIgniter autoloader configuration file and add your class to it, or change the actual autoloader.
You really should consider not using CodeIgniter :)
I have class MY_Controller extends CI_Controller and common logic for big profile section, so I'va tried to create class Profile extends MY_Controller with common logic for profile section and all class related to this section should extends this Profile class as I understand right, but when I tried to create class Index extends Profile I recieve an error:
Fatal error: Class 'Profile' not found
CodeIgniter tries to find this class in index.php which I am running.
Where is my mistake? Or maybe there is anoter better way to mark out common logic?
I take it you have put your MY_Controller in /application/core, and set the prefix in the config.
I would be careful about using index as a class name though. As a function/method in Codeigniter it has a dedicated behaviour.
If you then want to extend that controller you need to put the classes in the same file.
E.g. In /application core
/* start of php file */
class MY_Controller extends CI_Controller {
public function __construct() {
parent::__construct();
}
...
}
class another_controller extends MY_Controller {
public function __construct() {
parent::__construct();
}
...
}
/* end of php file */
In /application/controllers
class foo extends MY_Controller {
public function __construct() {
parent::__construct();
}
...
}
or
class bar extends another_controller {
public function __construct() {
parent::__construct();
}
...
}
I found this page on Google because I had the same problem. I didn't like the answers listed here so I created my own solution.
1) Place your parent class in the core folder.
2) Place an include statement at the beginning of all classes that include the parent class.
So a typical controller might look like this:
<?php
require_once APPPATH . 'core/Your_Base_Class.php';
// must use require_once instead of include or you will get an error when loading 404 pages
class NormalController extends Your_Base_Class
{
public function __construct()
{
parent::__construct();
// authentication/permissions code, or whatever you want to put here
}
// your methods go here
}
The reason I like this solution is, the whole point of creating a parent class is to cut down on code repetition. So I don't like the other answer that suggested copy/pasting the parent class into all of your controller classes.
It is possible with Codeigniter 3. Just including the parent file is enough.
require_once(APPPATH."controllers/MyParentController.php");
class MyChildController extends MyParentController {
...
All classes you are extending should live in application/CORE directory so in your case both My_Controller and Profile should live there. All "end point" controllers will live in application/controllers folder
UPDATE
I stand corrected. Extended classes should live in the same file. #Rooneyl's answer shows how to implement
After some struggle with version 3 and this issue I decided this was not a bad solution...
require_once BASEPATH.'core/Controller.php';
require_once APPPATH.'core/MYCI_Controller.php';
to add this second line where the first exists in the system/core/CodeIgniter.php
[If it's not too late, I recommend strongly against php and/or CodeIgniter.]
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