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.
Related
I'm trying to make an application in CodeIgniter where every controller extends a base controller called 'Incyte_Controller.php' and where every model extends a base model called 'Incyte_Model.php'.
I used to import these parent classes in every child class using 'require', but want to change that as it is too redundant for me.
So i moved the base files to 'application/core' and changed the base class prefix in Config.php from 'MY_' to 'Incyte_'.
Now, all controllers can extend 'Incyte_Controller' without the use of 'required'.
But, strangely, when the application tries to load a model it says:
Fatal error: Cannot instantiate abstract class Incyte_Model in
/opt/lampp/htdocs/incyte/system/core/Common.php on line 172
This happens even though i try to extend the base model in the exact same way i extended the base controller.
I checked for spelling errors, including capital letters, but found none.
Also, i must mention that both base classes are abstract classes, because they shouldn't operate on themselves(my teachers would most likely prefer that)
Please understand i KNOW abstract classes themselves cannot be instantiated, but classes that extend abstract classes CAN be instantiated, and that is what i'm trying to do. But it doesn't seem to work in one case, while it does work in another very similar case, which is strange.
I hope any of you can help
A better way to do this would be to extend the core model class to something like MY_Incyte_Model and then extend this in your actual models.
So instead of class AwesomeModel extends CI_Model you can have class AwesomeModel extends MY_Incyte_Model.
Read more here
https://ellislab.com/codeigniter/user-guide/general/core_classes.html
Go to /opt/lampp/htdocs/incyte/system/core/Common.php and you will find a line trying to do something like this:
new Incyte_Model();
The Incyte_Model class is abstract (which means that it can't be used directly - it has to be sub-classed).
Look into how PHP handles Object Oriented (OO) code.
In particular, look at class abstractions.
I have added a few personalized methods to the BaseContent class in Symfony
however there are other developers working on the Symfony app and probably they are having code generated by some sort of CRUD generator (I am not really familiar with Symfony so I am not sure what is being used).
The problem is that my bespoke code gets overwritten when the other developers re-generate the PHP classes.
Is there anything that can be done to keep the bespoke methods in the same BaseContent class file?
You add or override BaseContent parameters and methods in your Content class, which is generated by symfony commands propel:build-model or doctrine:build-model(depending on which ORM you use), and located parent directory of the Base class. This so that when changing your schema you don't have to rewrite your personalized methods accordingly.
Propel example:
// lib/model/om/BaseContent.php
// Don't touch as it may be overwritten
abstract class BaseContent extends BaseObject implements Persistent {
...
public function getParameter()
{
return $this->parameter;
}
...
}
// lib/model/Content.php
// add or override here
class Content extends BaseContent {
public function getParameter()
{
//Do something more
return parent::getParameter();
}
}
Anywhere within your symfony project you always use the non-Base class, which inherits the base class.
Base classes are generated by PHP ORMs Propel (under folders 'om' and 'map') or Doctrine (under folder 'base') basing them on what is defined in your schema.
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.
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.
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.