A lot of frameworks out there decided to use this approach: force the user to extend a base controller class (if you want to create a new controller) or to extends a base model class (if you want to create a new model).
Let's take a look at the code of CodeIgniter's controller base class:
/**
* Constructor
*/
public function __construct()
{
self::$instance =& $this;
// Assign all the class objects that were instantiated by the
// bootstrap file (CodeIgniter.php) to local class variables
// so that CI can run as one big super object.
foreach (is_loaded() as $var => $class)
{
$this->$var =& load_class($class);
}
$this->load =& load_class('Loader', 'core');
$this->load->initialize();
log_message('debug', "Controller Class Initialized");
}
What does it do? Well, as far as I can see, it just allows us to use $this->load->... for example.
Let's take a look at the __get() magic method of the model base class:
/**
* __get
*
* Allows models to access CI's loaded classes using the same
* syntax as controllers.
*
* #param string
* #access private
*/
function __get($key)
{
$CI =& get_instance();
return $CI->$key;
}
It does exactly the same thing. Now what does this way of doing things bring?
PRO
You can access useful CI classes by $this->....
CONS
You have to force the user to extends the base class
You have to force the user to call the parent::__construct() in the class construct
get_instace() is reserved
$this->instance redefinition cause a fatal error
You have basically repeated the same code both in the Model base class and the Controller base class
Now let's take a look at another approach:
Create a static class, such as App that do all the things the base controller does:
For example, $this->load->... would be App::load->....
Now consider pros and cons again:
PRO
You can access useful CI classes by App::....
You don't have to force the user to extends the base class
You don't have to force the user to call the parent::__construct() in the class construct
no methods name or properties name are reserved
You can use App both in the Model and in the Controller
CONS
You have no more the $this-> sexy syntax???
QUESTION
Here it comes the real question: would be the second a better or worse approach compared to the CI one? Why?
PRO
You can access useful CI classes by App::....
You don't have to force the user to extends the base class
You don't have to force the user to call the parent::__construct() in the class construct no methods
name or properties name are reserved
This not entirely valid. CI never force dev to extend the base class. All core framework functionality could be easily extended. You can have MY_Controller.php within application/core folder, contain your own base class, eg:
Front_Controller extends CI_Controller{
// Share common properties or functionalities across front/public controllers here
}
Admin_Controller extends CI_Controller{
// Share common properties or functionalities across administrative controllers here
}
Then, parent::parent_method() is very common in PHP. Mostly you'll have this syntax elsewhere, if you really use OO design in your application. This enable you to adding functionality to a subclass without loosing the inherited functionality from parent class.
So answering your question :
Here it comes the real question: would be the second a better or worse
approach compared to the CI one? Why?
Both attemps can be considered legal, atm. Because, the fact that : 1) there is no consistency checking (something like instanceof CI_Controller in PHP 5, or is_a for PHP4) within CI bootstrap, 2) And, CI not force you to returning anything from a controller action method (a Response object, like in SF, for example).
Thats to say, you can have an arbitrary class act as a controller. In fact you didn't need to wrap core Controller functionality within a static class, no one stoped you to use get_instance()->load->library('foo') and get_instance()->load->database() within those arbitrary class.
Related
I'm building a simple MVC framework in PHP and I'm stuck at the part where I have to create a BaseController class.
Every "page" controller needs to extend from this BaseController. Because this BaseController class will have properties that will give the user access to a template engine and a Logger class (and some other things).
The problem I have is that I'm not sure how to instantiate those things in the BaseController class. I can obviously hard code it in the __constructo() like this:
class BaseController
{
protected $view;
protected $log;
public function __construct()
{
$this->view = new \namespace\view('param1', 'param2');
$this->log = new Logger('name');
$this->log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));
}
}
But this doesn't make it very modular. It makes it hard to change the Logger class for example. Or if the user wants to change the template engine to Smarty for example.
I also can't really use a form of Dependancy Injection. Because then every controller that extends from the BaseController will have to pass those instances to the BaseController.
That would look something like this:
class BaseController
{
protected $view;
protected $log;
public function __construct($view, $log)
{
$this->view = $view;
$this->log = $log;
}
}
HomeController
class HomeController extends BaseController
{
public function __construct($view, $log)
{
parent::__construct($view, $log);
// Do my own stuff
}
}
To me that's not really user friendly when a user only wants to do a simple thing in the __constructor. Not even with an IoC.
So the only thing that I can think of now is to use a Service Provider from the __construct method in the BaseController. But I'm not really sure if that's the way to go? Maybe there is a better alternative?
So what would be a good solution to solve this problem?
PS. And if I do need a Service Locator, are there any good examples out there? The ones I've found were written by people that seemed like they didn't really know what they were talking about. They were basically copying each others blog posts.
You first need to think what's a controllers responsibility, why would it need access to logging functionality or why would it be setting the template engine or switching templates?
The controllers job is simply to extract the data from the request(mostly form data/user input) and sending it to the model layer via a service, it should not be selecting templates, that's the views job.
You obviously want you keep the dependencies of your base controller to a minimum so that when instantiating child controllers you don't have a big list of dependencies to inject.
My base controller has two dependencies, the request object and a view which are both injected in. Controllers are simple and light, no logic or anything fancy should happen in them. Once ever I had to use a logger in one of my controllers but that was just because PayPal was sending a request to it using the IPN system and I needed to log all the data in the request to a file to see what was going on, other than special cases like that I can't see why a controller would need a logger. In this case only the controller which dealt with the PayPal request held an instance of a logger, not the parent base controller.
You should always inject your dependencies. Don't instantiate objects in constructors, it makes your class tightly coupled to those objects.
If you end up using a service locator it can be a sign that your classes break the single responsibility principle of OOP.
Controllers are meant to be simple, don't over complicate them by adding in dependencies you think they might need in the future, especially not to the base controller or you may have a lot of refactoring to do.
I call a CodeIgniter controller method -imgupload- from jquery ajax. This controller extends my custom front controller.
class newad extends My_Controller{
public function __construct() {
parent::__construct();
}
public function imageupload() {
$this->load->library("uploadhandler");
}
The imgupload method calls the uploadhandler class which extends from newad.
class uploadhandler extends newad {
The functionality of that class works properly, except for one thing, I cant access the properties of the My_Controller class, even though they are declared protected.
The inheritance chain looks like this: My_Controller->newad->uploadhandler.
Any idea why I cant access those properties?
In short the answer is you do not need to extend Controller class here. You can just pass the value to your library as a parameter.
$params = array('user_ud' => $this->userID, 'otehr' => 'other');
$this->load->library('uploadhandler', $params);
//Now from your library
class uploadhandler{
public function __construct($params)
{
// Do something with $params
}
//.. Your code...//
}
Now about your question:
The functionality of that class works properly, except for one thing, I cant access the properties of the My_Controller class, even though they are declared protected. The inheritance chain looks like this: My_Controller->newad->uploadhandler. Any idea why I cant access those properties?
As inheritance chain are ok, you can access property of My_Controller from your library but not the value of the Current controller, because these two are different object.
So here is my answer how can we access the value? One way I have already mentioned. That will be enough if you need to share some property with the library. But what if you need to access all the Controller instance. There is a function to get the reference of controller instance get_instance(). You can use this function anywhere and get access of all public property controller. If you need to access any private property of controller the define a geter function to access that.
Explanation
First of all you need to learn basic about OOP. Learn about Class, Object, Inheritance..
When I said property of My_controller is different from the same property the you accessed from uploadhandler, it may confused you if you are not familiar with class and object. Here is two instance(object) of different class.
For short let say you have some classes like: Vehicle, Car, Machine and Person. All they have common attributes say name, weight ..
So, can we just inherit Any of these class from other??
Simple answer is no. We can't(!) define a Person class extending from Others. So how can we decide which incoherence would legal. If you can say Foo is a Bar you can write Foo class extending from Bar. Now from your case, It is obvious uploadhandler is not a controller. So Never Extend a anything from something that is not something.
NB: The answer is generic. If you need any specific clarification, just ask, I can update my answer
I am creating a reporting library in PHP and developed an abstract class named ReportView. This will provide the basic functionality of a report like Generating header and footer, create parameter form.
There will be another function named generate_report in this class. Currently it is empty in abstract class as at this level we do not know the contents of report. Further it includes a render function which calls this generate_report function and sends output to browser.
So I need whenever a child class inherits from ReportView it must implement
the generate_report method otherwise PHP must give error. Is there any keyword or method through which we can enforce implemetation of a specific function.
Do the following:
abstract class ReportView {
abstract protected function generate_report();
// snip ...
}
class Report extends ReportView {
protected function generate_report() { /* snip */ }
}
Any class that extends ReportView and is not abstract must implement generate_report (and any other abstract function in its super classes for that matter).
Sounds like you’d be better off creating an interface, which would enforce you to define those methods in classes that then implement this interface.
<?php
interface ReportInterface {
public function generate();
}
class MyReportClass implements ReportInterface {
}
Instantiating MyReportClass here will throw a fatal error, telling you generate() has not been implemented.
Edit: You can also create abstract classes that implement this interface. You can have your abstract class contain any methods all extending classes need, and have your interface define any methods you need to be defined by extending classes.
You need to declare the method as abstract as well (and don't give it a method body), otherwise the derived classes will not be forced to implement it.
Alternatively, you could implement the method but have it just throw an Exception (not sure why you would want to do this).
Lastly, if all the methods in your base class are "abstract" (do not have bodies) then you can make the class into an Interface instead.
I haven't used abstract classes much in practice, though I understand what they are : a way to dictate to subclasses which methods must be implemented.
I just put a Kohana project through Doxygen and can see the class hierarchy in its entirety. I see that, at the top of the chain we have a factory:
abstract class Kohana_Model {
public static function factory($name){
// Add the model prefix
$class = 'Model_'.$name;
return new $class;
}
}
Inherited directly below that, we have an empty abstract class:
abstract class Model extends Kohana_Model {}
... And below that, there are three inherited classes: Kohana_Model_Database, Kohana_ORM, and Model_Foobar.
Would someone please explain the programming reasoning for this - what is the purpose of having an empty abstract class this high up in the chain? (and, at all?)
Why not have Kohana_Model_Database, Kohana_ORM, and Model_Foobar inherit directly from Kohana_Model, when there is (apparently?) no other branching or inheritance going on between Model and Kohana_Model?
Answers you're seeking for are Cascading File System and Transparent Extensions.
It allows you to create a model by calling
class News_Model extends Model
by default, and that will automatically then extend Kohana_Model and things will be hunky dory.
It also lets you extend Kohana_Model by creating your own Model file
class Model extends Kohana_Model
which overrides the abstract Model class, and allows you to add custom functionality. Then, when you upgrade your Kohana version to (say) 3.4, your extended Model doesn't get overwritten by the new Kohana files.
I am about to develop a set of custom Ajax/rss/etc functions, both abstract, and then ones to be used in controllers. I was thinking of separating these methods based on return type.
I have a controller which will be enormous if I don't break down the logic.
I was thinking maybe a module - like
Modules/Admin/AnalyticsController
Modules/AjaxApi/AnalyticsController
Modules/RssApi/AnalyticsController
Any advice is appreciated!
Have you considered using or overriding or creating your own Context Switcher. You can read more here http://framework.zend.com/manual/en/zend.controller.actionhelpers.html
This has the features you need without needing to create new controllers for each action.
You can create your own abstract class with common methods. But in PHP you can to inherit only 1 class. Make your abstract class extends from Zend_Controller Action. Example code:
abstract class AjaxRssEtc extends Zend_Controller_Action
{
// code
}
class Ajaxapi_AnalyticsController extends AjaxRssEtc
{
// code
}
And your controller Ajaxapi_AnalyticsController will have methods from Zend_Controller_Action and your abstract class.