I would like to decorate every controller action that I have in my project.
I would like to start a custom logger engine and stop it every time an action method is called.
Like this:
class HomeController
{
public function indexAction()
{
Logger:start();
someLogicInThere();
...
Logger:end();
}
}
I would like to automate that extending to some class or something like this.
My final class controller will look like this:
class HomeController extends ControllerDecorator
{
public function indexAction()
{
someLogicInThere();
...
}
}
BTW: the logger stuff doesn't really matter. My question is more a design pattern issue
Any great idea?
You can use the built-in methods beforeAction (http://www.yiiframework.com/doc/api/1.1/CController#beforeAction-detail) and afterAction (http://www.yiiframework.com/doc/api/1.1/CController#afterAction-detail), inside your ControllerDecorator.
This way you would be able to decorate every class tha extends ControllerDecorator.
ps: I'm assuming you are using Yii. Right?
Your problem is called Cross-cutting concern. One way to handle this is by Aspect-oriented programming.
You can fake this in PHP >= 5.4 with Traits:
trait Logger
{
public function indexAction()
{
echo 'logging before<br>';
parent::indexAction();
echo 'logging after<br>';
}
}
class HomeControllerBase
{
public function indexAction()
{
echo 'indexAction<br>';
}
}
class HomeController extends HomeControllerBase
{
use Logger;
}
$homeController = new HomeController();
$homeController->indexAction();
As an experiment I've automated this process of writing a base class:
https://github.com/bitworking/AOP
I would not really use this in production because it needs to much resources.
Better look into AOP frameworks that parses the doctype and caches the new created class.
Not tested:
http://go.aopphp.com/
https://github.com/AOP-PHP/AOP
Another way to handle cross cutting concerns is to use event driven programming. This does not separate it 100% like AOP but it's a more approved design pattern especially in PHP.
Related
I have to call the method of another controller.
I use following code to make a call.
app('App\Http\Controllers\ApiUserController')->getList();
This is working fine.
But I want to try using use function so that I dont have to repeat all line
use App\Http\Controllers\ApiUserController;
class MyMethods
{
public function index()
{
app('ApiUserController')->getList()
Did I made some mistake here?
Instead of using app function, you will need to go through OOP way like so:
use App\Http\Controllers\ApiUserController;
class MyMethods
{
public function index()
{
$apiUserController = new ApiUserController();
$apiUserController->getList();
However, as many people have mentioned here, it is not really the best practice to call a method of one controller from the another.
So if I were at your place, I would create a helper, register its alias in config and use that helper to get the list in both places.
I hope it helps
Calling controller from other controller or other objects is not a good practice. Here is a good article explaining why. Also "fat" controllers is less preferable than "thin" controllers.
You should define a service layer object with common logic and use it. Create a service object and register it with one of service providers.
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\YourUserService;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(YourUserService::class);
}
}
After that you can use your service in DI style.
use App\Services\YourUserService;
class MyMethods
{
protected $userService;
public function __construct(YourUserService $userService)
{
$this->userService = $userService;
}
public function index()
{
$this->userService->foo();
}
}
Why should I use dependency injection?
I agree with the answer Learner has given above, however, I wouldn't recommend it in terms of code organisation and testability.
Looking at the code, I can see that you need to get list of users and thats why you have to call the api user controller from another controller. However, you can easily extract the logic out to a service or even a trait.
if you were to use a trait then you could do some thing like following,
trait ApiUser {
public function getList()
{
// get the list for users from api
}
}
//Then you can simply use this trait any where you want,
class SomeController
{
// correct namespace for ApiUser trait
use ApiUser;
}
Another way of doing it, which i love to use again and again depending on the scenario; is to stick with the principle of coding to interface not to implementation. That would be some thing like follow.
interface ApiUserInterface
{
public function getList();
}
class ApiUser implements ApiUserInterface
{
public function getList()
{
// logic to get users from api
}
}
Make sure that when application requires the interface, it knows where to find its implementation. If you using Laravel, then you could register your interface to class in AppServiceProvider
Once that's done, you can use this service any where you want as a contract.
class OneController
{
protected $apiUserContract;
public function __construct(ApiUserInterface $apiUserContract)
{
$this->apiUserContract = $apiUserContract;
}
public function index()
{
// You can retrieve the list of the contract
$this->apiUserContract->getList();
}
}
// you could also just typehint the contact in method without requiring
// it in constructor and it will get resolved out of IOC i.e. container
class AnotherController
{
public function index(ApiUserInterface $apiUserContract)
{
// You can retrieve the list of the contract
$apiUserContract->getList();
}
}
Let me know if you need further explanation and hope it helps
I wanna ask about design pattern.
Why should I use dependency injection in constructor, not import it ('use statement')?
For example:
in my controller:
class AuthController extends Controller {
public function __construct(UserGateway $userGateway)
{
$this->userGateway = $userGateway;
}
public function doSomething()
{
$this->userGateway->foo();
}
}
Why don't use just like this instead?
use Acme\UserGateway;
class AuthController extends Controller {
public function doSomething()
{
UserGateway::foo();
}
}
Many thanks.
Assuming UserGateway is not laravel facade: here's the biggest advantage of injecting stuff this way: in the future, you might redefine what UserGateway actually is, and supply some other class (most often, it's subclass) instead of it like this:
$this->app->bind(UserGateway::class, function ($app) {
return new NewUserGateway();
});
This is extremely useful for overriding some parts of your code, especially if you're using same packages across multiple projects. And it does not require you to change the AuthController's code.
If UserGateway is a Facade, the only benefit you'll get is a better code navigation with you IDE, since it will know what class exactly are you referencing (assuming that you didn't re-bound it).
I have seen some similar questions but I have yet to find a good solution for this from the interface all the way to the controller.
My Problem:
I have a few different kinds of applications that will require restarts, each has its own logic for restarting the application(SSH,API calls, etc.). I have set up an interface because although different logic, they all will need some similar functions. I have also created 3 classes, one for each app that implements that interface. where I am having issues is understanding the best way to keep the logic as abstracted from the controller as possible.
Some Questions:
Should I also be creating an Abstract class?
Should this be one controller that handles all types and chooses the correct one?
do I simply inject the different classes into the controller?
Code:
RestartInterface.php
<?php namespace Application\Service\Restart;
interface RestartInterface {
public function start();
public function stop();
public function restart();
}
example of implementing class:
<?php namespace Application\Service\Restart\AppOne;
use Application\Service\Restart\RestartInterface;
class AppOneRestart implements RestartInterface {
public function start() {
}
public function stop() {
}
public function restart() {
}
}
How could I use a service provider to keep this as modular as possible?
What is the best practice in this situation, I would like to be able to use many or as little restart implementations as I want.
thanks!
An abstract class is a way to create a base class you don't need your developers instantiating directly because, usually, there is still missing code from it, like, methods were not fully implemented. So you create an abstract which implements the common methods of your concrete restart classes
abstract class Restart {
public function restart() {
}
}
And then you implement one by one of those classes extending your abstract and creating the missing methods:
class AppOneRestart extends Restart implements RestartInterface {
public function start() {
}
public function stop() {
}
}
Option 1
If your whole application can use a single implementation of it and you just need the ability to swap from one to another, because your business somehow changed, a simple binding will do the trick:
App::bind('RestartInterface', 'AppOneRestart');
Option 2
If during a request you might need one or another, you probably will need to implement the Factory pattern: http://en.wikipedia.org/wiki/Factory_method_pattern, so you inject the factory in your controller:
class RestartApiController extends Controller {
public function __construct(RestartFactory $factory)
{
$this->restart = $factory->make('api');
}
}
i would like to know if it is possible to have a function in PHP which returns an interface or a class which contains an interface?
i tried something like this, but it fails
<?php
//class for list of controllers for ACL
class Gestionale_Action_Helper_Crud extends Zend_Controller_Action_Helper_Abstract {
interface crud_controller
{
public function indexAction();
public function modificaAction();
public function cancellaAction();
public function creaAction();
}
public function getCrudInterface(){
return $this->crud_controller;
}
}
what i wanted to do, in zend framework, create an interface that crud controllers must implement, or even better if i could create an abstract controller and have them implement that
thank you
I'd suggest that you use Zend_Rest_Controller instead of creating your own interface.
Zend_Rest_Controller is an abstract class that defines five basic methods you need in a CRUD-controller: index, get, post, put, and delete.
Combined with Zend_Rest_Route it lets you create nice and clean RESTful application.
You can get more reading on Zend_Rest_Controller at http://weierophinney.net/matthew/archives/228-Building-RESTful-Services-with-Zend-Framework.html and http://techchorus.net/create-restful-applications-using-zend-framework
Just place the interface outside of any class (preferably in a different file) and let it be implemented by all your crud-controllers.
<?php
class GrudController implements CrudInterface
{
// ...
}
i'm not sure i get what it is you want to do, but i'm fairly certain you're asking the wrong question. if you simply want to make sure an object implements a certain interface, this is quite easy to do. lets say for example you have some helper method in a class which deals with a crud controller, you just specify the type in the argument list:
class crud_helper {
public function help(crud_controller $cc) {
$cc->indexAction();
}
}
now you can pass any object that is an instance of a class that implements crud_controller to the method help. but no other object.
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