I'm trying to create my custom driver using Codeigniter
Files structure:
/libraries
/Test_driver
/drivers
Test_driver_first_driver.php
Test_driver.php
Driver super class:
class Test_driver extends CI_Driver_Library
{
function __construct()
{
$this->valid_drivers = array('test_driver_first_driver');
}
}
Driver Subclass :
class Test_driver_first_driver extends CI_Driver
{
function index()
{
echo "Hello world!";
}
}
Testing code in welcome.php Controller :
$this->load->driver('test_driver');
$this->test_driver->test_driver_first_driver->index();
but the output was : "Invalid driver requested Test_driver.test_driver_first_driver".
Does any one have any idea, Unfortunately Codeigniter user guide does not contains steps for creating custom driver.
its best practice or i should say my thinking that i always avoid underscores in parent class for the driver
so for me the file structure is some what like this
/libraries
/Testdriver
/drivers
Testdriver_first_driver.php
Testdriver.php
Testdriver.php
<?php
class Testdriver extends CI_Driver_Library
{
function __construct()
{
$this->valid_drivers = array('testdriver_first_driver');
}
}
Testdriver_first_driver.php
<?php
class Testdriver_first_driver extends CI_Driver
{
public function index()
{
echo "Hello world!";
}
}
In controller
$this->load->driver('testdriver');
$this->testdriver->first_driver->index();
Note : even if you don't use ucfirst() it will still work
i.e. Folder testdriver
Files -
testdriver.php (class testdriver extends CI_Driver_Library)
and
testdriver_first_driver.php (class testdriver_first_driver extends CI_Driver)
hope it is helpful. :)
I tried Karan's answer but I removed the parent's name in valid_drivers' value:
<?php
class Testdriver extends CI_Driver_Library{
function __construct(){
$this->valid_drivers = array('first_driver');
}
}
?>
This worked for me, you might want to give it a try. Credits to Karan.
I have just grappled with this in CodeIgniter v2.2.0 so thought I'd chip in. The scant documentation on custom drivers isn't too helpful as the example does not show the complete setup. The existing core CodeIgniter drivers are not organised in a consistent way either, with the driver parent class files being in different directory locations to where the docs say they should be etc. so you have little to go by but to consult the core Driver Library code.
In your given situation, the driver is seen as invalid is because you are effectively adding on the parent class name twice when calling it. This:
$this->test_driver->test_driver_first_driver->index();
Should be changed to:
$this->test_driver->first_driver->index();
Looking at the core code that the driver parent class extends:
class CI_Driver_Library {
protected $valid_drivers = array();
protected $lib_name;
// The first time a child is used it won't exist, so we instantiate it
// subsequents calls will go straight to the proper child.
function __get($child) {
if (!isset($this->lib_name)) {
$this->lib_name = get_class($this);
}
// The class will be prefixed with the parent lib
$child_class = $this->lib_name . '_' . $child;
Note the last line there. Basically, CI was trying to load a driver class named "Test_driver_test_driver_first_driver", which of course didn't exist.
For codeignaiter 3 problem in core system libraries driver.php
testdriver_first_driver.php
class Testdriver_first_driver extends CI_Driver {
public function index()
{
echo "Hello world!";
}
}
testdriver.php
class Testdriver extends CI_Driver_Library{
function __construct(){
$this->valid_drivers = array('first_driver');
}
}
CodeIgniter SPL Autoloader
/*
|--------------------------------------------------------------------------
| Autoloader function
|--------------------------------------------------------------------------
|
| Add to the bottom of your ./application/config/config.php file.
|
| #author Brendan Rehman
| #param $class_name
| #return void
*/
function __autoloader($class_name)
{
// class directories
$directories = array(
APPPATH . 'core/',
// add more autoloading folders here� and you�re done.
);
// for each directory
foreach ($directories as $directory)
{
// see if the file exsists
if (file_exists($directory.$class_name.'.php'))
{
require_once($directory.$class_name.'.php');
// only require the class once, so quit after to save effort (if
you got more, then name them something else
return;
}
}
}
spl_autoload_register('__autoloader');
Related
I'm building an application, now i'm created a helper
class Students{
public static function return_student_names()
{
$_only_student_first_name = array('a','b','c');
return $_only_student_first_name;
}
}
now i'm unable to do something like this in controller
namespace App\Http\Controllers;
class WelcomeController extends Controller
{
public function index()
{
return view('student/homepage');
}
public function StudentData($first_name = null)
{
/* ********** unable to perform this action *********/
$students = Student::return_student_names();
/* ********** unable to perform this action *********/
}
}
this is my helper service provider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class HelperServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
foreach(glob(app_path().'/Helpers/*.php') as $filename){
require_once($filename);
}
}
}
i event added it as an alias in config/app.php file
'Student' => App\Helpers\Students::class,
Try putting use App\Helpers\Student; at the top of your controller beneath the namespace delcaration:
namespace App\Http\Controllers;
use App\Helpers\Student;
class WelcomeController extends Controller
{
// ...
Look more into PHP namespaces and how they are used, I believe you may have a deficient understanding about them. Their only purpose is to make so you can name and use two classes with the same name (e.g. App\Helpers\Student vs maybe App\Models\Student). If you needed to use both of those classes inside of the same source file, you can alias one of them like this:
use App\Helpers\Student;
use App\Models\Student as StudentModel;
// Will create an instance of App\Helpers\Student
$student = new Student();
// Will create an instance of App\Models\Student
$student2 = new StudentModel();
You do not need to have a service provider for this, just the normal language features. What you would need a service provider for is if you wanted to defer the construction of your Student object to the IoC:
public function register()
{
$app->bind('App\Helpers\Student', function() {
return new \App\Helpers\Student;
});
}
// ...
$student = app()->make('App\Helpers\Student');
You should never have to include or require a class file in laravel because that is one of the functions that composer provides.
You do not need a service provider to make it works. Just lets the Students class as you did:
class Students{
public static function return_student_names()
{
$_only_student_first_name = array('a','b','c');
return $_only_student_first_name;
}
}
all its methods should be static
You added the Facade correctly:
'Student' => App\Helpers\Students::class,
Finally, looks like your problem is caused by forgetting a backslash at facade name. Uses \Students instead of Students:
public function StudentData($first_name = null)
{
$students = \Student::return_student_names();
}
When using a facade, it is not necessary makes nay include, the facades were made to avoid complex includes in everywhere.
I want to extend/overwrite the method logAttempt in class Confide (Confide on GitHub) in order to execute some extra code whenever someone logs in successfully. This would be cleaner than copying the same code to all controllers where logAttempt is called.
I read through the Laravel documentation and several answers here on stackoverflow, but I just can't get it working.
I created a new folder app/extensions with a file named Confide.php:
<?php
namespace Extensions;
class Confide extends \Zizaco\Confide\Confide {
public function __construct(ConfideRepository $repo) {
die('no way!');
$this->repo = $repo;
$this->app = app();
}
public function logAttempt($credentials, $confirmed_only = false, $identity_columns = array()) {
die('yeah man!');
}
}
I added the directory to my app/start/global.php:
ClassLoader::addDirectories(array(
// ...
app_path().'/extensions',
));
I also added it to composer.json and ran composer dump-autoload:
"autoload": {
"classmap": [
...,
"app/extensions"
]
},
My own Confide class seems not to be loaded at all, because Confide works as normal – without ever die()-ing.
And if I use \Extensions\Confide::logAttempt($input, true); in my controller including the namespace, I get this ErrorException:
Non-static method Extensions\Confide::logAttempt() should not be called statically, assuming $this from incompatible context
Do I really need my own ConfideServiceProvider class as well? I tried that, too, but I'm not sure at all what to put in there to make Confide use my extended class.
Is there no simple way to extend a tiny bit of a class? There must be, I'm just missing something here.
If you are looking to execute some code when a user logs in, you should just listen for that event. In this case, I believe Confide uses the Auth class to login, so you should be able to listen for that event.
Event::listen('auth.login', function($user)
{
$user->last_login = new DateTime;
$user->save();
});
I find this much easier and cleaner than worrying about extending classes.
EDIT: Made a mistake
I think you need to call the method like this:
\Extensions\Confide->logAttempt($input, true);
because you are using:
\Extensions\Confide::logAttempt($input, true);
Which is how you call static methods.
I think I finally figured it out.
I had to extend ConfideServiceProvider as well like so:
<?php
namespace Extensions;
class ConfideServiceProvider extends \Zizaco\Confide\ConfideServiceProvider {
/**
* Bootstrap the service provider.
*
* #return void
*/
public function boot() {
$this->package('extensions/confide');
}
/**
* Register the application bindings.
*
* #return void
*/
protected function registerConfide() {
$this->app->bind('confide', function($app) {
return new Confide($app->make('confide.repository'));
});
}
}
The code above goes into app/extensions/ConfideServiceProvider.php. Note: In boot() I replaced "zizaco" with "extensions" and in registerConfide() I made no changes at all, but if this method is not present in the extended class, the original class will be used. I've got no idea why.
Then in app/config/app.php I replaced Zizaco\Confide\ConfideServiceProvider with Extensions\ConfideServiceProvider.
My own extended Confide class looks like this now:
<?php
namespace Extensions;
class Confide extends \Zizaco\Confide\Confide {
public function logAttempt($credentials, $confirmed_only = false, $identity_columns = array()) {
$result = parent::logAttempt($credentials, $confirmed_only, $identity_columns);
if ($result) {
// Login successful. Do some additional stuff.
\Log::info('User ' . \Auth::user()->username . ' logged in.');
}
return $result;
}
}
Note: If you want to use any other standard Laravel class like Log, Session etc., prefix it with one backslash as shown in the example above, or add a use operator for each class you use (e.g. use \Log;).
I want to instantiate a class every time a page is loaded in CodeIgniter.
It looks like the /application/config/autoload.php is the place to do this. Is that correct?
I added this line to the package's autoload:
$autoload['packages'] = array('/application/third_party/Autoload.php');
Now I need this code to be executed on every page, where can I make this happen?
$bugsnag = new Bugsnag_Client("YOUR-API-KEY-HERE");
set_error_handler(array($bugsnag, "errorHandler"));
set_exception_handler(array($bugsnag, "exceptionHandler"));
To auto load a package (according to CI), you should put the package path/name in following array, like
$autoload['packages'] = array(APPPATH.'third_party', '/usr/local/shared');
But it doesn't execute any code automatically but makes your package available to use without explicitly loading it.
To make some code execute every time, you can put that code in your base controller's constructor function. Also, you can put the code in your config.php file. If you have an extended base controller, like application/core/MY_Controller.php
class MY_Controller extends CI_Controller {
//
}
Then you can use it's constructor function like
class MY_Controller extends CI_Controller {
function __construct()
{
parent::__construct();
$this->bugsnag = new Bugsnag_Client("YOUR-API-KEY-HERE");
set_error_handler(array($bugsnag, "errorHandler"));
set_exception_handler(array($bugsnag, "exceptionHandler"));
}
}
Rest of your controllers will use/extend MY_Controller instead of CI_Controller.
But you can also use a hook in this case (to register custom exception handlers), in application/config/hooks.php file, put following code
$hook['pre_controller'][] = array(
'class' => 'CustomExceptionHook',
'function' => 'SetExceptionHandlers',
'filename' => 'CustomExceptionHook.php',
'filepath' => 'hooks'
);
Create a class in application/hooks/CustomExceptionHook.php folder, like
class CustomExceptionHook
{
public function SetExceptionHandlers()
{
// add package path (if not auto-loaded)
$this->load->add_package_path(APPPATH.'third_party/package_folder/');
// load package (if not auto-loaded)
$this->load->library('Bugsnag_Client');
set_error_handler(array($this->Bugsnag_Client, "errorHandler"));
set_exception_handler(array($this->Bugsnag_Client, "exceptionHandler"));
}
}
Well let me explain it how you can do it.
As you have autoloaded the package its fine now you need to do this.
Create a MY_Controller in application/core/ directory.
Class MY_Controller Extends CI_Controller{
public $bugsnag = '';
public function __construct(){
parent::__construct();
$this->bugsnag = new Bugsnag_Client("YOUR-API-KEY-HERE");
set_error_handler(array($bugsnag, "errorHandler"));
set_exception_handler(array($bugsnag, "exceptionHandler"));
}
}
Note $this->bugsnag contains the object now. When you need to access it in any page you can simply do it like this by extending the parent class
Class Test Extends MY_Controller{
public function __construct(){
parent::__construct();
}
public function index(){
echo '<pre>';
print_R($this->bugsnag);
}
}
Here is a version of MY_Controller.php
This is using BugSnag via composer install
Using this method exposes the $this->_bugsnag variable to the entire CI System
class MY_Controller extends CI_Controller {
// Application Version
public $_app_version;
// Bugsnag
public $_bugsnag = NULL;
/**
* Constructor
*/
public function __construct() {
parent::__construct();
// Dont print errors to screen
ini_set('display_errors', 0);
// Load configs
$this->load->config('appversion');
$this->load->config('bugsnag');
$this->_app_version = $this->config->item('app_version');
// INIT: bugsnag
// https://docs.bugsnag.com/platforms/php/other/configuration-options/
$this->_bugsnag = Bugsnag\Client::make( $this->config->item('bugsnagAPIKey') );
$this->_bugsnag->setErrorReportingLevel( E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED );
$this->_bugsnag->setNotifyReleaseStages( ['developement', 'testing', 'production'] );
$this->_bugsnag->setReleaseStage( ENVIRONMENT );
$this->_bugsnag->setAppType( 'API Server' );
$this->_bugsnag->setAppVersion( $this->_app_version );
$this->_bugsnag->setHostname( $_SERVER['HTTP_HOST'] );
$this->_bugsnag->setProjectRoot( realpath(APPPATH) );
$this->_bugsnag->setFilters( ['password'] );
Bugsnag\Handler::register( $this->_bugsnag );
// Load Helpers
// Load Libraries
// Load Languages
}
}
You can now access the BugSnag methods like this.
$this->_bugsnag->leaveBreadcrumb( 'Hello' );
$this->_bugsnag->notifyException( $e );
Create a MY_Controller & inherit all your controllers off that. You can find more on this by Googling "MY_Controller"
I have a home controller with an index action that displays a set of featured products. However, the products are managed through a product controller including a proprietary model and views.
How do I access product information from within the index action in the home controller? Instancing product won't work as the class isn't loaded at runtime and CodeIgniter doesn't provide a way to dynamically load controllers. Putting the product class into a library file doesn't really work, either.
To be precise, I need the product views (filled with data processed by the product controller) inserted in the index view. I'm running CodeIgniter 2.0.2.
Load it like this
$this->load->library('../controllers/instructor');
and call the following method:
$this->instructor->functioname()
This works for CodeIgniter 2.x.
If you're interested, there's a well-established package out there that you can add to your Codeigniter project that will handle this:
https://bitbucket.org/wiredesignz/codeigniter-modular-extensions-hmvc/
Modular Extensions makes the CodeIgniter PHP framework modular. Modules are groups of independent components, typically model, controller and view, arranged in an application modules sub-directory, that can be dropped into other CodeIgniter applications.
OK, so the big change is that now you'd be using a modular structure - but to me this is desirable. I have used CI for about 3 years now, and can't imagine life without Modular Extensions.
Now, here's the part that deals with directly calling controllers for rendering view partials:
// Using a Module as a view partial from within a view is as easy as writing:
<?php echo modules::run('module/controller/method', $param1, $params2); ?>
That's all there is to it. I typically use this for loading little "widgets" like:
Event calendars
List of latest news articles
Newsletter signup forms
Polls
Typically I build a "widget" controller for each module and use it only for this purpose.
In this cases you can try some old school php.
// insert at the beggining of home.php controller
require_once(dirname(__FILE__)."/product.php"); // the controller route.
Then, you'll have something like:
Class Home extends CI_Controller
{
public function __construct()
{
parent::__construct();
$this->product = new Product();
...
}
...
// usage example
public function addProduct($data)
{
$this->product->add($data);
}
}
And then just use the controller's methods as you like.
Just to add more information to what Zain Abbas said:
Load the controller that way, and use it like he said:
$this->load->library('../controllers/instructor');
$this->instructor->functioname();
Or you can create an object and use it this way:
$this->load->library('../controllers/your_controller');
$obj = new $this->your_controller();
$obj->your_function();
Based on #Joaquin Astelarra response, I have managed to write this little helper named load_controller_helper.php:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
if (!function_exists('load_controller'))
{
function load_controller($controller, $method = 'index')
{
require_once(FCPATH . APPPATH . 'controllers/' . $controller . '.php');
$controller = new $controller();
return $controller->$method();
}
}
You can use/call it like this:
$this->load->helper('load_controller');
load_controller('homepage', 'not_found');
Note: The second argument is not mandatory, as it will run the method named index, like CodeIgniter would.
Now you will be able to load a controller inside another controller without using HMVC.
Later Edit: Be aware that this method might have unexpected results. Always test it!
With the following code you can load the controller classes and execute the methods.
This code was written for codeigniter 2.1
First add a new file MY_Loader.php in your application/core directory. Add the following code to your newly created MY_Loader.php file:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
// written by AJ sirderno#yahoo.com
class MY_Loader extends CI_Loader
{
protected $_my_controller_paths = array();
protected $_my_controllers = array();
public function __construct()
{
parent::__construct();
$this->_my_controller_paths = array(APPPATH);
}
public function controller($controller, $name = '', $db_conn = FALSE)
{
if (is_array($controller))
{
foreach ($controller as $babe)
{
$this->controller($babe);
}
return;
}
if ($controller == '')
{
return;
}
$path = '';
// Is the controller in a sub-folder? If so, parse out the filename and path.
if (($last_slash = strrpos($controller, '/')) !== FALSE)
{
// The path is in front of the last slash
$path = substr($controller, 0, $last_slash + 1);
// And the controller name behind it
$controller = substr($controller, $last_slash + 1);
}
if ($name == '')
{
$name = $controller;
}
if (in_array($name, $this->_my_controllers, TRUE))
{
return;
}
$CI =& get_instance();
if (isset($CI->$name))
{
show_error('The controller name you are loading is the name of a resource that is already being used: '.$name);
}
$controller = strtolower($controller);
foreach ($this->_my_controller_paths as $mod_path)
{
if ( ! file_exists($mod_path.'controllers/'.$path.$controller.'.php'))
{
continue;
}
if ($db_conn !== FALSE AND ! class_exists('CI_DB'))
{
if ($db_conn === TRUE)
{
$db_conn = '';
}
$CI->load->database($db_conn, FALSE, TRUE);
}
if ( ! class_exists('CI_Controller'))
{
load_class('Controller', 'core');
}
require_once($mod_path.'controllers/'.$path.$controller.'.php');
$controller = ucfirst($controller);
$CI->$name = new $controller();
$this->_my_controllers[] = $name;
return;
}
// couldn't find the controller
show_error('Unable to locate the controller you have specified: '.$controller);
}
}
Now you can load all the controllers in your application/controllers directory.
for example:
load the controller class Invoice and execute the function test()
$this->load->controller('invoice','invoice_controller');
$this->invoice_controller->test();
or when the class is within a dir
$this->load->controller('/dir/invoice','invoice_controller');
$this->invoice_controller->test();
It just works the same like loading a model
According to this blog post you can load controller within another controller in codeigniter.
http://www.techsirius.com/2013/01/load-controller-within-another.html
First of all you need to extend CI_Loader
<?php
class MY_Loader extends CI_Loader {
public function __construct() {
parent::__construct();
}
public function controller($file_name) {
$CI = & get_instance();
$file_path = APPPATH.'controllers/' . $file_name . '.php';
$object_name = $file_name;
$class_name = ucfirst($file_name);
if (file_exists($file_path)) {
require $file_path;
$CI->$object_name = new $class_name();
}
else {
show_error('Unable to load the requested controller class: ' . $class_name);
}
}
}
then load controller within another controller.
There are plenty of good answers given here for loading controllers within controllers, but for me, this contradicts the mvc pattern.
The sentence that worries me is;
(filled with data processed by the product controller)
The models are there for processing and returning data. If you put this logic into your product model then you can call it from any controller you like without having to try to pervert the framework.
Once of the most helpful quotes I read was that the controller was like the 'traffic cop', there to route requests and responses between models and views.
I know this is old, but should anyone find it more recently, I would suggest creating a separate class file in the controllers folder. Pass in the existing controller object into the class constructor and then you can access the functions from anywhere and it doesn't conflict with CI's setup and handling.
You can use this:
self::index();
I am using the following code to initialize a model from within my controller:
$this->load->model('model_name');
Is it possible to modify the above line somehow so that the model constructor recieves a parameter? I want to use the following code in the model constructor:
function __construct($param_var) {
parent::Model();
$this->$param_var = $param_var; //I'm not even sure this works in PHP..but different issue
}
This would be very helpful so that I can reuse my model classes. Thanks.
UPDATE:
(from one of the answers, my original question is solved..thanks!)
Just to explain why I wanted to do this: the idea is to be able to reuse a model class. So basically to give a simple example I would like to be able to pass an "order_by" variable to the model class so that I can reuse the logic in the model class (and dynamically change the order-by value in the sql) without having to create a separate class or a separate function.
Is this poor design? If so could you please explain why you wouldn't do something like this and how you would do it instead?
You can't pass parameters through the load function. You'll have to do something like:
$this->load->model('model_name');
$this->model_name->my_constructor('stuff');
In the model:
function my_constructor($param_var) {
...
}
Response to update:
You could just pass the order_by value when you're calling your model function. I'm assuming in your controller action, you have something like $this->model_name->get($my_id); Just add your order_by parameter to this function. IMO this makes your model logic more flexible/reusable because the way you were doing it, I assume setting order_by in the constructor will set the order_by value for every function.
In model
<?php
/* Load Model core model */
/* BASEPATH = D:\xampp\htdocs\ci_name_project\system\ */
include BASEPATH . 'core\\Model.php';
class User_model extends CI_Model {
/* Properties */
private $name;
/* Constructor parameter overload */
public function __construct($name) {
$this->set_name($name);
}
/* Set */
public function set_name($name) {
$this->name = $name;
}
/* Get */
public function get_name() {
return $this->name;
}
}
in controller
<?php
class User_controller extends CI_Controller {
public function index() {
/* Load User_model model */
/* APPPATH = D:\xampp\htdocs\ci_name_project\application\ */
include APPPATH . 'models\\User_model.php';
$name = 'love';
/* Create $object_user object of User_model class */
$object_user = new User_model($name);
echo $object_user->get_name(); // love
}
}
I see your reasoning for this, but may I suggest looking at Object-Relational Mapping for your database needs. There is a user-made ORM library for CodeIgniter called DataMapper that I've been using lately. You can use tables in your controllers as objects, and it may be a better fit for your problem.
Instead of using DataMapper i suggested to use IgnitedRecord because that the DataMapper is no longer maintained more over it has been replaced into Ruby
I am using CI ver 3.X, so what I am about to say is it will work for Codeigniter 3.X (and I haven't checked ver 4+ yet).
When I went thru the source code of the function model() in file system/libraries/Loader.php, noticed that it does not support loading the model with construct parameters. So if you want to make this happen you have to change the source code (bold, I know, and I just did).
Down below is how I did it.
1. Firstly, replace line 355
$CI->$name = new $model();
with some modifications:
$_args_count = func_num_args();
if(3 < $_args_count){
$refl = new ReflectionClass($model);
$CI->$name = $refl->newInstanceArgs(array_slice($_args_count, 3));
}else{
$CI->$name = new $model(); // origin source code
}
2. Load the model with a bit difference:
$this->load->model("model_name", "model_name", false, $param_var); // where amazing happens
Now you can have $this->model_name as you wished.