Im current trying to learn more about the core of OpenCart and how its classes actually work. Im also trying to advance my OOP skills in general as Im still learning in that area, so perhaps theres something obvious that Im not seeing.
Im wondering how a controller file knows how to find the cart class (for example).
E.g.
In catalog/controller/checkout cart there is (obviously with code removed)
class ControllerCheckoutCart extends Controller {
public function index() {
$this->cart->update();
}
}
The Controller class can be found in system/engine/controller.php
update() can be found system/library/cart.
I assumed that in the controller.php there would be a link to the cart class, or an object made from it. (Im basing that on the use of $this->).
So how is the cart class actually found from the controller?
Thank you
Firstly, your ControllerCheckoutCart extends the Controller class, so this is the class we need to focus on. You can find this class in /system/engine/controller.php.
Inside this class, there are two magic methods we are interested in. The first is the __construct, where the "registry" class is loaded (found in /system/engine/registry.php if you're interested in picking that apart - it's very simplistic).
You can think of this as a lookup of all the classes the store uses, such as model files, library files and so on. In the construct, the registry is passed to the controller so it has a reference to it
public function __construct($registry) {
$this->registry = $registry;
}
The second and more important magic method is the __get method. This is called when a classes property doesn't exist, for you to handle it yourself if you wish to do so. OpenCart uses this to try and get the class with that key from the registry
public function __get($key) {
return $this->registry->get($key);
}
So $this->cart in any controller would try to get the object with the key cart from the registry. If you look at the index.php file you will see this is allocated in there
// Cart
$registry->set('cart', new Cart($registry));
ControllerCheckoutCart extends Controller, which means it inherits all the code in Controller which you are not seeing here. Some code in Controller, likely in Controller::__construct, is creating the $this->cart object. Example:
class Controller {
public function __construct() {
$this->cart = new Cart;
}
}
Since this constructor is inherited by all child classes, they construct their own $this->cart as well and have access to it in their own methods.
As mentioned by Jay Gilford, you need to register your newly added library class file in the index.php and/or admin/index.php (depending on if you are using it in catalog or admin)
$registry->set('yourlibraryclass', new YourLibraryClass());
so that upon system loading, Opencart knows that your class exists, then you can call all its functions by:
$this->yourlibraryfilename->function();
Please note that your library file name is normally the same as your class name, hence it is used in the example here.
After the change has been done in the index.php files, you need to logout and login again to see the changes.
Related
I'am a Brazilian developer, so... sorry for my limited English right away.
Well, in fact my problem is more a convention problem because until now I hadn't use services with Laravel (my apps were that simple so far).
I read about it before ask this question, but nothing helped with this specific situation. I'll try to describe in a objective way.
before that, just a comment: I know about the mistake using just controllers in these example. The ask is really about that mistake.
Well, the actual structure is:
abstract class CRUDController extends Controller {
protected function __construct($data, $validatorData) {
// store the data in a attribute
// create with Validator facade the validation and store too
}
abstract protected function createRecord();
protected function create() {
try {
// do the validation and return an Response instance with error messages
// if the data is ok, store in the database with models
// (here's where the magic takes place) in that store!
// to do that, calls the method createRecord (which is abstract)
$this->createRecord();
// return a success message in an Response instance
}
catch(\Exception $e) {
// return an Response instance with error messages
}
}
}
class UserController extends CRUDController {
public function __construct($data) {
parent::__construct($data, [
'rules' => [
// specific user code here
],
'messages' => [
// specific user code here
],
'customAttributes' => [
// specific user code here
]
]);
}
protected function createRecord() {
$user = new UserModel();
// store values here...
$user->save();
return $user;
}
}
// here's the route to consider in that example
Route::post('/user', 'WebsiteController#register');
class WebsiteController extends Controller {
private $request;
public function __construct(Request $request) {
$this->request = $request;
}
public function register() {
$user = new UserController();
$user->create($this->request);
// here's the problem: controller working with another controller
}
}
class UserAPIController extends Controller {
// use here the UserController too
}
and many other classes that extends CRUDController in the same way...
What I want
I want to create a controller (called here as CRUDController) to reuse methods like the pattern says (create, read, update and delete).
To be really objective here I'll use the create method as an example.
With the code above it seems clear the purpose? I think so... all my controllers have that code of validation equal and reusable. That's the thing.
Besides that, I want to my route of website call another controller (UserController) to store new users... but in the same way, I'll create an API that uses the same controller in the same way (with validations etc). That's the purpose of Responses in the CRUDController (I'll read them in the WebSiteController to resolve what to do, like show a view and in the other hand with the API I'll basically return the Response.
My real problem
Convention and pattern. The MVC pattern is broken here. Controller calling another controller is wrong and I know that.
I want to know what thing I should use! Services? Is that right? I see a lot (really) of examples of services but nothing like that, working with models and reusing code, etc. I never use Services but I know how to use, but I don't know if it's right to these cases.
I really hope that someone can help here and sorry once again for the mistakes with the English. Thanks a lot.
You're calling the CRUD controller a controller but it does not behave as an MVC controller. At best it's just a helper class. You could always do this:
abstract class CRUDManager {
//As you had the CRUDController
}
class UserManager extends CRUDManager {
//As you had the UserController
}
In your AppServiceProvider:
public function boot() {
$app->bind(UserManager::class, function ($app) {
return new UserManager(request()->all()); //I guess that's what you need.
});
}
Whenever you need to use it you can do:
public function register(UserManager $user) {
$user->create();
}
Now one thing to point out. It's not a good idea to initialise the request in the constructor. You should use dependency injection in controller methods. I don't even know if the request is available when the controller is being constructed (I know the session is not). The reason why I say this is that the middleware runs after the controller is constructed and therefore the request may be modified when the controller method is called.
Another note: If you did the original solution because you needed to use certain controller methods, then you can just use the corresponding traits (because the controller itself does not really have many method). I'm guessing a trait like ValidatesRequests would be one you'd need to use.
I'll answer my own question. I use a pattern called Repository Pattern to resolve the problem (or I try to use, because it's the first time using this pattern: maybe I don't use in the right way in every steps).
Files structure
Controllers
UserController.php
Models
UserModel.php
Providers
UserRepositoryServiceProvider.php
Repositories
RepositoryInterface.php
Repository.php
User
UserRepositoryInterface.php
UserRepository.php
Traits
InternalResponse.php
With that structure I did what I wanted in my question without working just with controllers.
I create a trait called InternalResponse. That trait contains a few methods that receive a transaction, validate if it's the case and then return a Response (called "internal" in my logic because the controller will read and maybe change the Response before return it in the end).
The Repository class, which is abstract (because another class must extend it to make sense to use. In this case the class UserRepository will extend...), uses the Trait mentioned.
Well, with it in mind, it's possible to know that the UserController uses the UserRepositoryInterface, that provides an object UserRepository: because the UserRepositoryServiceProvider register this with that interface.
I think there's no need to write code here to explain, because the problem is about an pattern, and these words explain well the problem (in the question) and the resolution with this answer here.
I'll write here a conclusion, I mean, the files structure with comments to explain a little bit more, to end the answer.
Conclusion: Files structure with comments
Controllers
UserController.php
// the controller uses dependency injection and call methods of
// UserRepository, read and changes the Response receveid to finally
// create the final Response, like returning a view or the response
// itself (in the case it's an API controller)
Models
UserModel.php
// an normal model
Providers
UserRepositoryServiceProvider.php
// register the UserRepositoryInterface to
// return a UserRepository object
Repositories
RepositoryInterface.php
// the main interface for the Repository
Repository.php
// the main repository. It's an abstract class.
// All the others repositories must extend that class, because
// there's no reason to use a class Repository without an Model
// to access the database... That class share methods like create,
// read, update and delete, and the methods validate and transaction
// too because uses the trait InternalResponse.
User
UserRepositoryInterface.php
// the interface for UserRepository class
UserRepository.php
// that class extend Repository and uses the UserModel
Traits
InternalResponse.php
// trait with methods like validate and transaction. the method
// validate, read and validate the data receveid for the methods
// create and update. and all the CRUD methods uses the method
// transaction to perform the data to the database and return a
// response of that action.
That's what I do and like I said before, I don't know if it's a hundred percent correct in reference to Repository Pattern.
I hope this can help someone else too.
Thanks for all.
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
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.
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