So i'm working on a framework (Yes i know it's overdone) and i was brainstorming what would be the best possible approach to achieve dynamic object creation inside a base controller. So i came up with the following solution:
Base controller
abstract class Controller extends Container
{
public function __get($obj)
{
$this->$obj = $this->get($obj)
return $this->obj;
}
{
App controller
class Welcome extends Controller
{
public function actionIndex()
{
$this->view->render('welcome');
}
}
So now let me explain whats happening. The base controller extends a container which is a dependency injection class. It holds closures for creating objects. So when we try to access undefined property in welcome controller it calls the magic __get method.
Then the base controller gets the binding from the DI container and defines it as a property so we can use it.
So everything works exactly as i wanted, but i know that magic methods are slow. I was wondering how would it impact performance when there is dozens of __get calls. Is there a better way of achieving the same thing without defining all classes manually in the base controller?
First of all, that is not a DI Container. What you have there is a Service Locator, which you have inherited. If you user Service Locator with some care, it is a quite good pattern, but people usually abuse it and it turns into an architectural anti-pattern.
When you write extends in your code, it means that when you write class B extends A, you are stating that B is just a special case of A. Controllers are not service locators.
Next .. emm. You seem to be missing the point of controllers withing MVC and MVC-inspired architectures.
A controller can send commands to the model to update the model's state (e.g., editing a document). It can also send commands to its associated view to change the view's presentation of the model (e.g., by scrolling through a document).
source: wikipedia
Basically, what you are trying to attemt would both violate the basic principles of the pattern and also ignore Single Responsibility Principle. You would be turning a controller in some variation on Factory.
What this boils down to is: why is your controller producing instance and returning them?
Related
Sorry if this is a duplicate question or a common design principle, I have searched around but was unable to find any answers to this question. I'm probably just searching with the wrong keywords.
I have been looking at a popular library Sabre/Event (https://sabre.io/event/) and in the code there is a simple class/inheritance model that I am trying to understand:
The class EventEmitter implements EventEmitterInterface and uses EventEmitterTrait (see below for code).
There is a comment in EventEmitterTrait above the class which says:
* Using the trait + interface allows you to add EventEmitter capabilities
* without having to change your base-class.
I am trying to understand why this comment says this, and why it allows adding capabilities without changing the base class, and how that is different from just putting the routines into EventEmitter itself.
Couldn't you just extend EventEmitter and add capabilities in the derived class?
Simplified code:
// EventEmitter.php
class EventEmitter implements EventEmitterInterface {
use EventEmitterTrait;
}
// EventEmitterInterface.php
interface EventEmitterInterface {
// ... declares several function prototypes
}
// EventEmitterTrait.php
trait EventEmitterTrait {
// ... implements the routines declared in EventEmitterInterface
}
You're basically asking two questions here.
What are interfaces and why are they useful?
What are traits and why are they useful?
To understand why interfaces are useful you have to know a little about inheritance and OOP in general. If you've ever heard the term spaghetti code before (it's when you tend to write imperative code that's so tangled together you can hardly make sense of it) then you should liken that to the term lasagna code for OOP (that's when you extend a class to so many layers that it becomes difficult to understand which layer is doing what).
1. Interfaces
Interfaces diffuse some of this confusion by allow a class to implement a common set of methods without having to restrict the hierarchy of that class. we do not derive interfaces from a base class. We merely implement them into a given class.
A very clear and obvious example of that in PHP is DateTimeInterface. It provides a common set of methods which both DateTime and DateTimeImmutable will implement. It does not, however, tell those classes what the implementation is. A class is an implementation. An interface is just methods of a class sans implementation. However, since both things implement the same interface it's easy to test any class that implements that interface, since you know they will always have the same methods. So I know that both DateTime and DateTimeImmutable will implement the method format, which will accept a String as input and return a String, regardless of which class is implementing it. I could even write my own implementation of DateTime that implements DateTimeInterface and it is guaranteed to have that method with that same signature.
So imagine I wrote a method that accepts a DateTime object, and the method expects to run the format method on that object. If it doesn't care which class, specifically, is given to it, then that method could simply typehint its prototype as DateTimeInterface instead. Now anyone is free to implement DateTimeInterface in their own class, without having to extend from some base class, and provide my method with an object that's guaranteed to work the same way.
So in relation to your EventEmitter example, you can add the same capabilities of a class (like DateTime) to any class that might not even extend from DateTime, but as long as we know it implements the same interface, we know for sure it has the same methods with the same signatures. This would mean the same thing for EventEmitter.
2. Traits
Traits, unlike interfaces, actually can provide an implementation. They are also a form of horizontal inheritance, unlike the vertical inheritance of extending classes. Because two completely different class that do not derive from the same base class can use the same Trait. This is possible, because in PHP traits are basically just compiler-assisted copy and paste. Imagine, you literally copied the code inside of a trait and just pasted it into each class that uses it right before compile time. You'd get the same result. You're just injecting code into unrelated classes.
This is useful, because sometimes you have a method or set of methods that prove reusable in two distinct classes even though the rest of those classes have nothing else in common.
For example, imagine you are writing a CMS, where there is a Document class and a User class. Neither of these two classes are related in any meaningful way. They do very different things and it makes no sense for one of them to extend the other. However, they both share a particular behavior in common: flag() method that indicates the object has been flagged by a user for purposes of violating the Terms of Service.
trait FlagContent {
public function flag(Int $userId, String $reason): bool {
$this->flagged = true;
$this->byUserId = $userId;
$this->flagReason = $reason;
return $this->updateDatabase();
}
}
Now consider that perhaps your CMS has other content that's subject to being flagged, like a Image class, or a Video class, or even a Comment class. These classes are all typically unrelated. It probably wouldn't make much sense just to have a specific class for flagging content, especially if the properties of the relevant objects have to be passed around to this class to update the database, for example. It also doesn't make sense for them to derive from a base class (they're all completely unrelated to each other). It also doesn't make sense to rewrite this same code in every class, since it would easier to change it in one place instead of many.
So what seems to be most sensible here is to use a Trait.
So again, in relation to your EventEmitter example, they're giving you some traits you can reuse in your implementing class to basically make it easier to reuse the code without having to extend from a base class (horizontal inheritance).
Per Sabre's Event Emitter's docs on "Integration into other objects":
To add Emitter capabilities to any class, you can simply extend it.
If you cannot extend, because the class is already part of an existing
class hierarchy you can use the supplied trait.
So in this case, the idea is if you're using your own objects that already are part of a class hierarchy, you may simply implement the interface + use the trait, instead of extending the Emitter class (which you won't be able to).
The Integration into other objects documentation says:
If you cannot extend, because the class is already part of an existing class hierarchy you can use the supplied trait".
I understand it's a workaround when you already have an OOP design you don't want to alter and you want to add event capabilities. For example:
Model -> AppModel -> Customer
PHP doesn't have multiple inheritance so Customer can extend AppModel or Emitter but not both. If you implement the interface in Customer the code is not reusable elsewhere; if you implement in e.g. AppModel it's available everywhere, which might not be desirable.
With traits, you can write custom event code and cherry-pick where you reuse it.
This is an interesting question and I will try to give my take on it. As you asked,
What is the purpose of using traits to define functions for an interface ?
Traits basically gives you the ability to create some reusable code or functionality which can then be used any where in your code base. Now as it stands, PHP doesn't support multiple inheritance therefore traits and interfaces are there to solve that issue. The question here is why traits though ?? Well imagine a scenario like below,
class User
{
public function hasRatings()
{
// some how we want users to have ratings
}
public function hasBeenFavorited()
{
// other users can follow
}
public function name(){}
public function friends(){}
// and a few other methods
}
Now lets say that we have a post class which has the same logic as user and that can be achieved by having hasRatings() and hasBeenFavorited() methods. Now, one way would be to simply inherit from User Class.
class Post extends User
{
// Now we have access to the mentioned methods but we have inherited
// methods and properties which is not really needed here
}
Therefore, to solve this issue we can use traits.
trait UserActions
{
public function hasRatings()
{
// some how we want users to have ratings
}
public function hasBeenFavorited()
{
// other users can follow
}
}
Having that bit of logic we can now just use it any where in the code where ever it is required.
class User
{
use UserActions;
}
class Post
{
use UserActions;
}
Now lets say we have a report class where we want to generate certain report on the basis of user actions.
class Report
{
protected $user;
public function __construct(User $user)
{
$this->user = $user
}
public function generate()
{
return $this->user->hasRatings();
}
}
Now, what happens if i want to generate report for Post. The only way to achieve that would be to new up another report class i.e. maybe PostReport.. Can you see where I am getting at. Surely there could be another way, where i dont have to repeat myself. Thats where, interfaces or contracts come to place. Keeping that in mind, lets redefine our reports class and make it to accept a contract rather than concrete class which will always ensure that we have access to UserActions.
interface UserActionable
{
public function hasRatings();
public function hasBeenFavorited();
}
class Report
{
protected $actionable;
public function __construct(UserActionable $actionable)
{
$this->actionable = $actionable;
}
public function generate()
{
return $this->actionable->hasRatings();
}
}
//lets make our post and user implement the contract so we can pass them
// to report
class User implements UserActionable
{
uses UserActions;
}
class Post implements UserActionable
{
uses UserActions;
}
// Great now we can switch between user and post during run time to generate
// reports without changing the code base
$userReport = (new Report(new User))->generate();
$postReport = (new Report(new Post))->generate();
So in nutshell, interfaces and traits helps us to achieve design based on SOLID principles, much decoupled code and better composition. Hope that helps
After developing a few projects using Codeigniter since 2 years, I stared to learn Laravel.
I downloaded a few projects lo learn how they are coded. As I understood, many of them are using only models, views and controllers which is same as Codeigniter.
But one project has used repositories and interfaces. It is really hard to understand whats going on that project. So what is the usage of repositories and interfaces in Laravel? When should I use them?
I will try to explain as clearly as possible the two concepts.
Interfaces\Contracts
In general OOP interfaces are used to describe which methods/functionalities the class that implements that interface is offering without caring about the actual implementation.
Laravel uses Contracts mainly to separate a service from the actual implementation. To be more clear let's make an example
<?php
namespace App\Orders;
class OrdersCache
{
protected $cache;
public function __construct(\SomePackage\Cache\Memcached $cache)
{
$this->cache = $cache;
}
public function find($id)
{
if ($this->cache->has($id)) {
//
}
}
}
As you can see in this class the code is tightly coupled to a cache implementation (i.e. \SomePackage\Cache\Memcached) so if the API of that Cache class changes our code also must be changed accordingly. The same thing happens if we want to change the Cache implementation with another one (e.g. redis).
Instead of doing that, our code could depend on an interface that is agnostic from the implementation:
<?php
namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class OrdersCache
{
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
public function find($id)
{
if ($this->cache->has($id)) {
//
}
}
}
Now our code is not coupled with any specific implementation because Cache is actually an interface. So basically in our class we are requiring an instance of a class that behaves like described in the Cache interface, but we are not really interested in how it works internally. Doing that if we want to change the cache implementation we could write a class that implements the interface Cache without changing any line of code in our OrdersCache class. Doing that our code is easier to understand and maintain and your packages are a lot more reusable. See the section Loose Coupling in the Laravel documentation for further examples.
Interfaces and Service Container
One of the main features of Laravel is its Service Container, it is used to manage dependencies and performing dependency injection. Please take a look at Service Container definition from Laravel documentation.
Dependency Injection is widely used by Laravel also to bind interfaces to implementation. Let's make an example:
$app->bind('App\Contracts\EventPusher', 'App\Services\RedisEventPusher');
And let our class be
<?php
namespace App\Http\Controllers;
use App\Contracts\EventPusher;
class EventsController extends Controller
{
protected $pusher;
public function __construct(EventPusher $pusher)
{
$this->pusher = $pusher;
}
}
Without declaring anything else we are basically saying everytime that someone need an EventPusher instance, please Laravel, provide an instance of RedisEventPusher class. In this case everytime that your controller is instantiated, Laravel will pass an instance of RedisEventPusher to your controller without specifying anything else.
You can dig into that by looking at Binding Interfaces to Implementation section on the Laravel documentation.
Repositories
Repositories is a concept applicable to the MVC pattern independently from any specific framework. Typically you have your Model that is the data layer (e.g. interacts with the database directly), your Controller that handles the access logic to the data layer and your View that shows the data provided by the Controller.
Repositories instead could be defined as follows:
To put it simply, Repository pattern is a kind of container where data access logic is stored. It hides the details of data access logic from business logic. In other words, we allow business logic to access the data object without having knowledge of underlying data access architecture.
Soruce: https://bosnadev.com/2015/03/07/using-repository-pattern-in-laravel-5
To know how to use them within Laravel please take a look at this great article.
That's all, i hope it helps to clear up your mind.
Interfaces are what any implementing class should call.
interface CanFlyInterface
{
public function fly();
}
Think of it like programming without bothering with logic.
if ($object instanceof CanFlyInterface) {
$obj->fly();
}
Now we could have passed a Bird object, or an Aeroplane object! PHP DOESN'T CARE, so long as it implements the interface!
class Bird implements CanFlyInterface
{
public function fly()
{
return 'flap flap!';
}
}
class Aeroplane implements CanFlyInterface
{
public function fly()
{
return 'roar! whoosh!';
}
}
Your other question, what a Repository class is. It's just a class that keeps all your DB queries in the one place. Check this interface as an example:
interface RepositoryInterface
{
public function insert(array $data);
public function update(array $data);
public function findById($id);
public function deleteById($id);
}
Hopefully this should clear things up for you! Good luck with all your PHP coding :-D
Let's start with the easier one, the interface:
You normally use interfaces to implement classes with required methods:
http://php.net/manual/en/language.oop5.interfaces.php
Laravel's Contracts are a set of interfaces that define the core services provided by the framework. For example, a Illuminate\Contracts\Queue\Queue contract defines the methods needed for queueing jobs, while the Illuminate\Contracts\Mail\Mailer contract defines the methods needed for sending e-mail.
https://laravel.com/docs/5.4/contracts#introduction
When Laravel is running it can check if a class implements a special interface:
if ($cls instanceof IInterface) {
$cls->interfaceFunction();
}
Since Laravel is able to work with queues it will check if the event should be queued or not by checking for an exiting interface.
To inform Laravel that a given event should be broadcast, implement the Illuminate\Contracts\Broadcasting\ShouldBroadcast interface on the event class.
https://laravel.com/docs/5.4/broadcasting#defining-broadcast-events
Repository:
I didn't found that much about this:
Our repository should not have so much knowledge regarding who is providing them data or how they are providing it. https://laravel.com/docs/5.4/contracts#loose-coupling
But I found some other information on a webpage:
a Repository will connect Factories with Gateways
https://code.tutsplus.com/tutorials/the-repository-design-pattern--net-35804
The link will give you more information about the the details.
Hope I could help you :)
First of all, using Repository and Interface in larger application is not only beneficiary in Laravel but in all technology for coding standard as well as for separation of concern.
According to Microsoft (I found best explanation here)
Why to use Repository:
Use a repository to separate the logic that retrieves the data and
maps it to the entity model from the business logic that acts on the
model. The business logic should be agnostic to the type of data that
comprises the data source layer. The repository mediates between the
data source layer and the business layers of the application. It
queries the data source for the data, maps the data from the data
source to a business entity, and persists changes in the business
entity to the data source.
A repository separates the business logic
from the interactions with the underlying data source or Web service.
The separation between the data and business tiers has three benefits:
It centralizes the data logic or Web service access logic. It provides
a substitution point for the unit tests. It provides a flexible
architecture that can be adapted as the overall design of the
application evolves. There are two ways that the repository can query
business entities. It can submit a query object to the client's
business logic or it can use methods that specify the business
criteria. In the latter case, the repository forms the query on the
client's behalf. The repository returns a matching set of entities
that satisfy the query.
For Interface, you have a lot of answers above, hope you have understand.
First of all, repositories and interfaces are not specific to Laravel but common coding standards in most of the languages.
Below Laracasts videos will be useful to understand the basics if you don't mind spend few dollars.
https://laracasts.com/lessons/repositories-and-inheritance
https://laracasts.com/series/object-oriented-bootcamp-in-php
I'm working on my PHP (H)MVC project in which I separated the views from the controllers - as in the answer How should a model be structured in MVC?. Their relationship is 1:1, so they have the same "actions". Therefore, in bootstrap.php, after I instantiate them I call:
// ... Controller and view are already instantiated.
call_user_func_array(array($controller, $actionName), $actionParameters);
call_user_func_array(array($view, $actionName), $actionParameters);
Let's say that a controller and a view become, each of them, a model (domain object) as constructor parameter.
Using Auryn dependency injection container, I try to share the same instance of the model between the controller and the view, without instantiating it beforehand. E.g. before controller and view instantiation takes place in bootstrap.php.
In his answer, tereško describes the use of a model/service factory. But as a "Note" he says:
...A much better implementation would have the DI container (like
Auryn) to create controllers and views, with only the required
services, instead of using a factory.
My question is: Can I implement this functionality without a model factory using the dependency injection container?
I'm kind of stuck in this task and I don't really know if this is somehow possible.
Thank you.
Yes, you can.
But it's kinda fiddly. You basically need to set up the service as "shared":
<?php
$injector->define('MailerService', [
':server' => 'fak.it',
':port' => '443',
]);
$injector->share('MailerService');
$controller = $injector->make('FooBarController');
This assumes that you controller was defined kinda like this:
<?php
class FooBarController
{
public function __construct(MailerService $service)
{
// ...
}
}
In this aspect the Symfony's standalone DI component is a bit easier to use, because you can put this kinda of configuration in a json or yaml file.
P.S. You probably should abstract your user input as some sort of Request object ant pass that in your controller on each method-call.
Kinda like this:
<?php
$request = new Request( .. something here maybe .. );
$controller->action($request);
Make for a bit nicer looking code :)
After some time without knowing how to do this properly, and avoid duplicating code in several controllers, I searched and again to seek, but can not find a clear answer.
One case in particular, need to calculate several statistics data completed of an entity. This calculation will use in 3 different controllers.
In two of them, I'll show broken down into different layouts, and on the third, I will use this data to make a global calculation.
business logic for each calculation, involves more than 100 lines of code, I would have to triple in the different controllers.
A scheme could be:
Calculation fields completed "Data A"
Calculation fields completed "Data B"
Calculation fields completed "Data C"
With these 3 values, I make a later total calculation.
The options that I could find are:
Define a controller as a service for reuse later in other controllers but I am not clear whether it is a good practice, here's a good article analyzing this issue Symfony2: Make my Controllers Services?
on the one hand How to Define Controllers as Services
and on the other a comment for Fabien Potencier Mention best practice "controller as a service" in chapter Controller # 457 and The 'New' symfony Best Practices
Create an abstract BaseController. Of which it is spoken here, but the idea is not developed.Symfony2 and be DRY approach in controllers
Any idea how to solve this scenario?
thanks a lot
The Services seem to be much corresponding with the described usage.
Recently, I worked on an accounting solution. I used a lot of complex algorithms and I soon had very long methods, even trying to optimise code.
Services are easily callable, and their usage can make controllers so much sexier, lighter, and make big methods readable and more cuttable by using separated services corresponding to specific actions.
And they can be used in other components, as long as DependencyInjection is present, it's a sufficient raison to move your logic if you may need apply it in another context that a controller.
It's simple to declare it :
services:
acmeapp.calculation:
class: Acme\AppBundle\Services\CalculationService
arguments: ["#doctrine.orm.entity_manager", "#acmeapp.anotherservice"]
And the service
class CalculationService {
protected $em;
protected $another;
public function __constructor(EntityManager $entityManager, AnotherService $anotherService)
{
$this->em = $entityManager;
$this->another = $anotherService;
}
//...
}
The Controller-Service approach is primarily a service, with all its advantages.
A method of your service can render a view and have a route associated using the _controller attribute, like this :
display_data_a:
path: /data/A
methods: GET
defaults: { _controller: acmeapp.calculation:dealWithData }
Without extending your service from the Symfony\Bundle\FrameworkBundle\Controller\Controller, but, of course, you can use it.
Also, an abstract BaseController can be a very clean alternative, if you have many duplicated code with a few different characters between your controllers.
It's what I'm doing before use services, if the methods are corresponding with my definition of a controller, which is corresponding to what #fabpot says in your link.
The DIC mostly helps manage "global" objects. Controllers are not global objects. Moreover, a controller should be as thin as possible. It's mainly the glue between your Model and the View/Templates. So, if you need to be able to customize then, it probably means that you need to refactor them and extract the business logic from them.
More about BaseController in OOP,
The way is simple, if you have a line of code that repeats itself two or three times in a method, you use a variable.
Same for a block of code, you will use a method.
For controllers it's the same, if you have two or more objects of the same type (here a controller), you should use an AbstractController (or BaseController), move your duplicated methods in it (only once, of course), and remove them from the child controllers.
BaseController :
class BaseController extends Controller
{
/**
* Shortcut for get doctrine entity manager.
*/
public function getEntityManager()
{
return $this->getDoctrine->getEntityManager();
}
/**
* Shortcut for locate a resource in the application.
*/
public function locateResource($path)
{
return $this->get('kernel')->locateResource($path);
}
// ...
}
And use it as part of your child controllers
class ChildController extends BaseController
{
public function helloAction()
{
$em = $this->getEntityManager();
$file = $this->locateResource('#AcmeAppBundle/Resources/config/hello.yml');
// ...
}
}
Hope this helps you to avoid a lot of a duplicated code.
I'm making my own primitive MVC framework with PHP, and I'm wondering where I should load/instantiate corresponding controller dependencies?
In the constructor of each controller (tightly coupled) or inject them (loosely coupled)?
The only part of the latter that I'm not too sure of is for the dependencies to be instantiated on bootstrap level, outside of the MVC paradigm, before being injected. Not every controller uses the exact same dependencies besides the default parent ones. I would have to instantiate them all, which would also create a lot of overhead.
I've seen some existing frameworks do it like $this->load->model('model'); // CodeIgniter in the constructor, but I have no clue on why they're doing it like that.
I would suggest you inject the dependencies, so your controllers are less coupled to your framework. This will make a switch to another framework easier.
About instantiating dependencies: I suggest you use (or implement) a dependency injection container. This container should contain factories that can instantiate services.
In an ideal situation your controllers are services too (meaning they too have factories in the dependency injection container).
This way only the controller you need for a particular request will be instantiated, and therefor only its dependencies are instantiated.
When building you own framework, this means that after the routing phase (when the correct controller is known), the framework should grab that controller from the container. The container itself will make sure all dependencies that are needed will be provided.
Have a look at Pimple for an example of a simple dependency injection container.
PS: That line from CodeIgniter looks a lot like the service locator pattern. This pattern is similar to dependency injection, but does not provide full inversion of control.
Q: Where should i load/instantiate corresponding controller dependencies?
There are multiple ways.
The load and instantiation concepts are basically "before/outside" and "after/inside".
Before and outside means, that you load the file containing a class (which you want to instantiate and pass to the controller), before you load the controller.
But how do you know, what the controller needs, before loading the controller? Uh..
Dependency Description Files
A description file comes into play, describing the wiring between your controller and it's dependencies. In other words, you can see the dependencies of your controller by looking at it's dependency description file. This concept is often used by Dependency Injection tools, which analyze the object and pull the dependencies names out automatically. It's also possible to maintain such a wiring configuration file manually. But it's tedious.
Service Locator
A Service Locator is a instantiation helper for dependencies.
Basically, it contains the same information like a dependency description file, but this time in form of a registry. The link between parts of your application becomes this registry.
Both strategies introduce overhead. It's a trade-off. When you change the perspective and look at things from an application with maybe 500+ classes, then you realize that a dependency injection tool is sometimes worth it.
Manual Injection
via Constructor Injection.
After and inside means, that you load the file containing your controller and then start to care about the dependencies.
At this point the class is not instantiated, yet, but the autoloader might do it's dirty deeds behind the scene. He evaluates the use statements at the top of your controller file. The use statements declare namespaced classes, which the autoloader resolves to actuall files and loads them. You might then start to use these classes as dependencies in your controller. This is probably the easiest way to solve your problem and i strongly suggest looking into the topics autoloading with namespaces and use-statements.
When the class is instantiated, you have the following possiblities:
use might use Setter Injection or Reference Injection to set the dependencies to the object. This requires that your Constructor Dependencies are already solved or your constructor is empty.
It's possible to combine these strategies.
Q: What does this do $this->load->model('model'); // CodeIgniter?
CodeIgniter is a legacy application framework. It was created in times, when namespaced autoloading wasn't available. $this->load is a basic class loading helper. This is the opposite of an "auto"loader, (which surprise, surprise) loads things automatically.
CodeIgniters loader class is used to load various other classes, like libraries or files from the view, helpers, models or user defined stuff. This is again the concept of a registry. Here the registry just knowns where things are in your application layout and resolves them. So $this->load->model('model'); means that the modelfunction must have some piecies of information, about the position of model files in your application.
You provide a model name and the path for the file is constructed by model.
And this is exaclty what it does (except a bit of overhead): https://github.com/EllisLab/CodeIgniter/blob/develop/system/core/Loader.php#L223.
Since I'm a Symfony developer, I can only give you a reference to Symfony.
I think you should do like they are doing in Symfony by thinking about what you need in each
Controller object.
At least, you need :
a Request object
and a Model loader object that gives you every Model you need.
Create a BaseController that implements these few functions and then extend it with custom Controllers.
You can also take a look on Silex : http://silex.sensiolabs.org/ a Micro Framework
Hope it helps.
When do you say "In the constructor" you mean to pass in the conatiner and pull the dependencies from them (in the constructor)?
<?php
class SomeController
{
public function __construct($container)
{
$this->service1 = $contanier->get('service1);
}
//...
}
I advice against that, though simpler and easier you will be coupling your controllers to the container thus using a ServiceLocator instead of truly inversion of control.
If you want your controllers to be easy unit-testable you should use inversion of control:
class SomeController
{
public function __construct($service1)
{
$this->service1 = $service1;
}
//...
}
And you can even create your controller as a service inside the container:
// this uses Pimple notation, I hope you get the point
$container['controller'] = function($c) {
return SomeController($c['service1']);
}
Use proxy services to lazy load them
Also if your controllers needs more than some services and you won't be using all of them you can:
1) Use proxy services in order to lazy load the service only when they are really needed
<?php
class ProxyService
{
/**
* #var Service1Type
*/
private $actualService;
public function __construct()
{
$this->actualService = null;
}
private function initialize()
{
$this->actualService = new Service1(); // This operation may take some time thus we deferred as long as possible
}
private function isInitialized()
{
return $this->actualService === null;
}
public function someActionOnThisService()
{
if (!$this->isInitalized()) {
$this->initalize();
}
$this->actualService->someActionOnThisService();
}
There you have a simple proxy object with lazy loading. You may want to check the fantastic Proxy Manager Library if you want to go that route
2) Split your controller
If your contoller has too many dependencies, you may want to split it.
In fact you may want to read the proposal by Paul M. Jones (lead developer of Aura Framework) about MVC-Refinement, IMHO is a good read even though you may not fully agree with it.
Even if you split your controller in order to reduce the dependencies, lazy loading your dependencies is a good idea (obviously you'll have to check weather if its doable in your context: more work in order to gain more speed).
Maybe you need to define __autoload() function before you try to load the Classes which is not loaded yet. Like:
function __autoload($className) {
require "/path/to/the/class/file/$className.php";
}
My example is very very simple to auto require the file which the class definition is in.
You can also use if-else statement or switch statement in that function to fit your own situations smartly.
The __autoload() function is called when PHP doesn't find the class definition, works for new, class_exists(), call_user_method(), etc, and absolutely for your dependences/parents classes. If there is still no class definition after __autoload() is called, PHP will generate an error.
Or you can use spl_autoload_register() function instead of __autoload() more gracefully.
For more information, you might want to see:
http://php.net/manual/en/function.autoload.php
http://php.net/manual/en/function.spl-autoload-register.php