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();
Related
I have written a custom yii\rest\UrlRule that scans a directory and adds any files named "*Controller.php" to the list of allowed REST API controllers in our application.
use yii\rest\UrlRule;
class RestWildcardUrlRule extends UrlRule
{
public $path = '#app/services/controllers';
public function init()
{
$d = dir(\Yii::getAlias($this->path));
$controllers = [];
while (($entry = $d->read()) !== false) {
if(strpos($entry, 'Controller.php')) {
$controllers[] = strtolower(str_replace(['Controller.php'], '', $entry));
}
}
$this->controller = $controllers;
parent::init();
}
}
This saves us from having to write specific UrlRules in our configuration for every controller class. All works nicely, except now we need to check if each controller extends a specific base class:
class SomeRestController extends RestControllerBase
Do I have to create an instance of each class in my UrlRule->init() method and use something like is_subclass_of, or is there an easier way to see if each controller extends RestControllerBase?
You could use
get_parent_class ( ... )
Retrieves the parent class name for object or class.
http://php.net/manual/en/function.get-parent-class.php
see also class_parent() as suggested byt Alex Blex
http://php.net/manual/en/function.class-parents.php
This is my controller
class CarsController extends Controller {
public function actionIndex() {
echo 1; exit();
}
}
this is the module file:
<?php
class CarsModule extends CWebModule {
public $defaultController = "cars";
public function init() {
// this method is called when the module is being created
// you may place code here to customize the module or the application
// import the module-level models and components
$this->setImport(array(
'cars.models.*',
'cars.components.*',
));
}
public function beforeControllerAction($controller, $action) {
if (parent::beforeControllerAction($controller, $action)) {
// this method is called before any module controller action is performed
// you may place customized code here
return true;
}
else
return false;
}
}
my problem is if I access my project like: localhost/cars it works. If I access localhost/cars/index I am getting this message: Unable to resolve the request . If I create a new function and I access like this: localhost/cars/myfunction, still the same. I am working on Windows. Can someone help me with this ?
Typically the default url rule for modules is module/controller/actin, so for access actionIndex inside CarsController inside CarsModule, your url should be localhost/cars/cars/index, not localhost/cars/index. If you don't like the url be like localhost/cars/cars/index, you can write one url manager rule to map localhost/cars/cars/index into localhost/cars/index. Something like this:
'cars/index' => 'cars/cars/index'
In my Zend application there is a layout file used in multiple modules. Now i need to retrieve data from database (table gateway) and display on layout. Then it should appear across all the modules.
How do i achieve that ?
Ex -
<?php echo $user_name; ?>
Value for $user_name should be taken from database and pass to layout file.
you dont have to set it in every controller. You could just attach it to a layout variable in your module.php.
public function onBootstrap(MvcEvent $e)
{
$sm = $e->getApplication()->getServiceManager();
$tableWhatever = $sm->get('tableWhatever');
$viewModel = $e->getApplication()->getMvcEvent()->getViewModel();
$viewModel->userName = $tableWhatever->getUserName();
}
Depending on the zf2 version you may have to access the variable in your layout like so:
$this->layout()->userName;
You also have the possibility to extend the AbstractActionController and add the layout variables trough that. I usually just go with the quick onBootstrap method though.
I believe, in Zend, your controller(s) will want:
$this->view->assign('variableName', 'variableValue');
And in your view(s), you will want:
$this->variableName;
You could use Zend Plugin to achieve something like that, like:
class MyPlugin extends Zend_Controller_Plugin_Abstract {
public function preDispatch(Zend_Controller_Request_Abstract $request) {
// Get instance
$layout = Zend_Layout::getMvcInstance();
$view = $layout->getView();
$view->user_name = 'your_username';
}
}
and register your plugin in frontController:
Zend_Controller_Front::getInstance()->registerPlugin(new MyPlugin());
then in layout , you could do:
<?php echo $this->user_name; ?>
Hi i have issue here of calling another controller action to send an mail, here is my code:
user.php
public function followAction()
{
$follow_id = $this->_getParam('id');
$response = "<a href='javascript: void(0)' class='i-wrap ig-wrap-user_social i-follow_small-user_social-wrap'><i class='i i-follow_small-user_social ig-user_social'></i>Stop Following</a>";
notifyEmail() ------> need to call Notity Controller with notifyEmail() Action along with
params
$this->_helper->json($response); ----> return json response
}
NotifyController.php
<?php
class NotifyController extends Zend_Controller_Action
{
public function init()
{
/* Initialize action controller here */
}
public function index()
{
}
public function notifyEmailAction()
{
// rest of email code goes here
}
}
Thanks for help!
You have to move send mails functionality to another place,
and call it in both methods.
Check this thread
Calling member function of other controller in zend framework?
I suggest to create at the path /library a new folder 'My' and in it new file Utilities.php and in that file a new class where you can put all your help methods
class My_Utilities {
// put here your custom help methods
}
You need to auto-load that namespace.In configs/application.ini put
autoloaderNamespaces.my = "My_"
Then you can use namespace My_ and class My_Utilities.
In any case, you can call method form another controller:
include 'NotifyController.php';
$a = new NotifyController($this->_request, $this->_response);
$a->notifyEmailAction();
$this->action('action', 'controller', 'module', 'params')
That view helper walk through frontController and dispatch all plugins again.
I think is not the best solution keep in mind wasting resources
Please try this code
$this->action("action","controller","module")
I have this thing that I need in multiple places:
public function init()
{
$fbLogin = new Zend_Session_Namespace('fbLogin'); #Get Facebook Session
if(!$fbLogin->user) $this->_redirect('/'); #Logout the user
}
These two lines:
$fbLogin = new Zend_Session_Namespace('fbLogin'); #Get Facebook Session
if(!$fbLogin->user) $this->_redirect('/'); #Logout the user
Whats the best way to do it in ZendFramework?To create a plugin or? I mean I want to execute it in multiple places but If I need to edit it I want to edit it in one place.
Here is an example of an Action Helper that you can call from your controllers easily.
<?php
class My_Helper_CheckFbLogin extends Zend_Controller_Action_Helper_Abstract
{
public function direct(array $params = array())
{
// you could pass in $params as an array and use any of its values if needed
$request = $this->getRequest();
$view = $this->getActionController()->view;
$fbLogin = new Zend_Session_Namespace('fbLogin'); #Get Facebook Session
if(!$fbLogin->user) {
$this->getActionController()
->getHelper('redirector')
->gotoUrl('/'); #Logout the user
}
return true;
}
}
In order to use it, you have to tell the helper broker where it will live. Here is an example code you can put in the bootstrap to do so:
// Make sure the path to My_ is in your path, i.e. in the library folder
Zend_Loader_Autoloader::getInstance()->registerNamespace('My_');
Zend_Controller_Action_HelperBroker::addPrefix('My_Helper');
Then to use it in your controller:
public function preDispatch()
{
$this->_helper->CheckFbLogin(); // redirects if not logged in
}
It doesn't go into much detail, but Writing Your Own Helpers is helpful as well.
If you need this check in every Controller you could even set up a baseController from which you extend instead of the default one:
class My_Base_Controller extends Zend_Controller_Action
{
public function init()
{ ...
class IndexController extends My_Base_Controller
{ ...
Shift your init() into the base controller and you don't need to repeat yourself in every specific controller.
Need a varying init() in a specific controller?
class FooController extends My_Base_Controller
{
public function init()
{
parent::init();
...