In my app, I was testing Google Directions API with ajax, but since I was just testing all the logic was in the routes.php file. Now I want to do things the proper way and have three layers: route, controller and service.
So in the routes I tell Laravel which method should be executed:
Route::get('/search', 'DirectionsAPIController#search');
And the method just returns what the service is supposed to return:
class DirectionsAPIController extends BaseController {
public function search() {
$directionsSearchService = new DirectionsSearchService();
return $directionsSearchService->search(Input::all());
}
}
I created the service in app/libraries/Services/Directions and called it DirectionsSearchService.php and copied all the logic I developed in routes:
class DirectionsSearchService {
public function search($input = array()) {
$origin = $input['origin'];
$destination = $input['destination'];
$mode = $input['mode'];
// do stuf...
return $data;
}
}
I read the docs and some place else (and this too) and did what I was supposed to do to register a service:
class DirectionsAPIController extends BaseController {
public function search() {
App::register('libraries\Services\Directions\DirectionsSearchService');
$directionsSearchService = new DirectionsSearchService();
return $directionsSearchService->search(Input::all());
}
}
// app/libraries/Services/Directions/DirectionsSearchService.php
use Illuminate\Support\ServiceProvider;
class DirectionsSearchService extends ServiceProvider {
}
I also tried adding libraries\Services\Directions\DirectionsSearchService to the providers array in app/config/app.php.
However, I am getting this error:
HP Fatal error: Class
'libraries\Services\Directions\DirectionsSearchService' not found in
/home/user/www/my-app-laravel/bootstrap/compiled.php on line 549
What am I doing wrong? And what is the usual way to use your own services? I don't want to place all the logic in the controller...
2 main things that you are missing:
There is a difference between a ServiceProvider and your class. A service provider in Laravel tells Laravel where to go look for the service, but it does not contain the service logic itself. So DirectionsSearchService should not be both, imho.
You need to register your classes with composer.json so that autoloader knows that your class exists.
To keep it simple I'll go with Laravel IoC's automatic resolution and not using a service provider for now.
app/libraries/Services/Directions/DirectionsSearchService.php:
namespace Services\Directions;
class DirectionsSearchService
{
public function search($input = array())
{
// Your search logic
}
}
You might notice that DirectionsSearchService does not extend anything. Your service becomes very loosely coupled.
And in your DirectionsAPIController.php you do:
class DirectionsAPIController extends BaseController
{
protected $directionsSearchService;
public function __construct(Services\Directions\DirectionsSearchService $directionsSearchService)
{
$this->directionsSearchService = $directionsSearchService;
}
public function search()
{
return $this->directionsSearchService->search(Input::all());
}
}
With the code above, when Laravel tries to __construct() your controller, it will look for Services\Directions\DirectionsSearchService and injects into the controller for you automatically. In the constructor, we simply need to set it to an instance variable so your search() can use it when needed.
The second thing that you are missing is to register your classes with composer's autoload. Do this by adding to composer.json's autoload section:
"autoload": {
"classmap": [
... // Laravel's default classmap autoloads
],
"psr-4": {
"Services\\": "app/libraries/Services"
}
}
And do a composer dump-autoload after making changes to composer.json. And your code should be working again.
The suggestion above can also be better with a service provider and coding to the interface. It would make it easier to control what to inject into your controller, and hence easier to create and inject in a mock for testing.
It involves quite a few more steps so I won't mention that here, but you can read more in Exploring Laravel’s IoC container and Laravel 4 Controller Testing.
Related
I'm using inversify-express-utils using the shortcut decorators (#GET, #POST...) within a node application.
Is it possible to inject middleware into the controller to use with these decorators?
Example of what I'm trying to achieve (doesn't work):
export class TestController implements Controller {
constructor(#inject(TYPES.SomeMiddleware) private someMiddleware: ISomeMiddleware) {}
#Get('/', this.someMiddleware.someMiddlewhereMethod())
public test() {
...
}
}
Like #OweR ReLoaDeD said, currently you can't do that with middleware injected through the controller constructor, due to the way decorators work in TypeScript.
However, you can achieve the same effect by wrapping the controller definition in a function that accepts a kernel, like so:
controller.ts
export function controllerFactory (kernel: Kernel) {
#injectable()
#Controller('/')
class TestController {
constructor() {}
#Get('/', kernel.get<express.RequestHandler>('Middleware'))
testGet(req: any, res: any) {
res.send('hello');
}
}
return TestController;
}
main.ts
let kernel = new Kernel();
let middleware: express.RequestHandler = function(req: any, res: any, next: any) {
console.log('in middleware');
next();
};
kernel.bind<express.RequestHandler>('Middleware').toConstantValue(middleware);
let controller = controllerFactory(kernel);
kernel.bind<interfaces.Controller>(TYPE.Controller).to(controller).whenTargetNamed('TestController');
let server = new InversifyExpressServer(kernel);
// ...
UPDATE
I added an example to the inversify-express-examples repo that showcases this approach using both custom and third-party middleware.
You should be able to use middleware please refer to the following unit tests as an example.
Update
I don't think that is possible because decorators are executed when the class is declared. The constructor injection takes place when the class instance is created (which is after it has been declared). This means that, when the decorator is executed, this.someMiddleware is null.
I'm afraid you won't be able to inject the middleware into the same class that uses it but you can do the following:
import { someMiddlewareMethod} from "middleware";
class TestController implements Controller {
#Get('/', someMiddlewareMethod())
public test() {
// ...
}
}
This is not a limitation of InversifyJS this is a limitation caused by the way decorators work.
Much ink has flowed about Sf2 controller/container. I face with follow situation:
app/console container:debug security
...
> 4
[container] Information for service security.token_storage
Service Id security.token_interface
Class Symfony\Component\Security\Core\Authentication\Token ...
...
Public yes
LoginBundle\DefaultController.php
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction()
{
dump(Controller::get('security.token_storage'));
...
works OK, obviously.
LoginBundle\UserUtilsController
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class UserUtilsController extends Controller
{
public function getRoleById()
{
dump(Controller::get('security.token_storage'));
...
throw: Error: Call to a member function get() on a non-object
In Sf2 Book - Service container I found:
In this example, the controller extends Symfony's base Controller, which gives you access to the service container itself. You can then use the get method to locate and retrieve the my_mailer service from the service container.
The misunderstanding is:
- Both controllers extends basic controller which itself extends ContainerAware which implements ContainerAwareInterface which set container.
- Both controllers access same public service container.
So, why the second controller it doesn't work?
I know that the question is old but I don't want to inject a controller as service and I think it is redundant and wrong to redeclare a public service in services.yml
Thank you in advance.
I found the answer myself and I want to share for every one is in same situation...
The UserUtilsController doesn't work because it's not working in this manner. The Symfony architecture is interesting if you get to know it.
LoginBundle\Controller\UserUtilsController
// For this job we don't need to extends any class..
class UserUtilsController
{
// but we need a property for injecting the service in it
private $token;
// Now let's inject service into our property $token
public function __construct($token)
{
$this->token = $token;
}
// It's not done but let pretend it is and let's use it
public function getRoleById()
{
...
return $this->token->getToken()->getRoles();
...
services.yml
#here it's the magic
services:
# this is a new services container
user.loggeduser_utils:
# this is my class (second class)
class: LoginBundle\Controller\UserUtilsController
# this is how I feed my _construct argument
arguments: ["#security.token_storage"]
So I just inject an existing service in my new class.
Now, to use this we must to call in first class:
LoginBundle\Controller\DefaultController.php
class DefaultController extends Controller
{
public function indexAction()
{
// because my class is now a service container we call in this way
$userRoleId = $this->get('user.loggeduser_utils');
...
This solution above is almost trivial simple AFTER understanding the Sf2 DI model.
i have laravel 4 installed in my wamp server. this what i did :
1-add this "app/classes" to composer.json.
2-create folder classes in app and put Curl.php class in that folder.
3-add this app_path().'/classes', to global.php inside app/start.
4-run composer dump-autoload in command in www directory.
5-for using like Curl::help() must add this alias to app/config/app.php aliases section 'Curl'=>'Curl' .
after doing this when i return return Curl::hello(); in router this page comes :
http://www.mediafire.com/view/h9489jr5s2699ty/err.PNG
my Curl's class : Curl class
any help??
This is not how Laravel aliases works, you need more code (create Facades and Service Providers) to make it work.
So you have some options:
1) Remove the Alias from app/config/app.php and instantiate your class:
$curl = new Curl;
$curl->help();
2) Instantiate your class and bind it to the IoC container, in global.php, filters.php or create a file for that:
App::bindShared('mycurl', function($app)
{
return new Curl;
});
And create a Facade:
<?php namespace MyClasses\Facades;
use Illuminate\Support\Facades\Facade;
class MyCurlFacade extends Facade {
protected static function getFacadeAccessor()
{
return 'mycurl';
}
}
Your Alias has to point to this Facade script file, like all the others you see in app.php.
'Curl' => 'MyClasses\MyCurlFacade',
And it should work like this Curl::hello();.
3) Create the usual (correct?) Laravel structure, which also includes a ServiceProvider to instantiate your class and bind it to the IoC container in the application Boot:
<?php namespace MyClasses;
use Illuminate\Support\ServiceProvider;
class MyCurlServiceProvider extends ServiceProvider {
protected $defer = false;
public function boot()
{
}
public function register()
{
{
$this->app['mycurl'] = $this->app->share(function($app)
{
return new MyCurl;
});
}
public function provides()
{
return array('mycurl');
}
}
THIS IS UNTESTED CODE, SO DO NOT EXPECT IT TO WORK IN THE FIRST RUN
I'm currently using SOA, I've a bunch of Service, (ArticleService, CommentService, UserService, etc..)
I also have a ConfigurationService which is filled from an XML configuration file.
I'm using Zend Framework.
THis configuration service is needed in some of my service, and I'm using dependency injection, is it a good practice, to add ConfigurationService in constructor of most my Service to be able to fetch global configuration?
Thank you for your feedbacks.
I would say, no, don't pass the config container - neither as a service nor as an array nor a Zend_Config instance - in the constructor of your other services. I would keep the injection (whether by constructor or by setter) for those services focused on the actual objects/collaborators/data they actually need.
So, for example, an ArticleService might depend upon an ArticleRepository interface/object or on an ArticleMapper or on a db adapter. Let the constructor/setter signatures for the ArticleService reflect what it truly needs.
Instead, what I would do is during Bootstrap, create some kind of factory object - perhaps as an application resource - that accepts in its constructor your config data/object/service (or even better, the bootstrap instance itself, from which you could get, not just your config data, but also any application resources, like a db adapter, that were created during the bootstrap process). Then write methods on your factory object that create/deliver the other services you need. Internally, the factory maintains a registry of already created services so that it can lazy-create instances where required.
A snippet of what I have in mind might be as follows:
Bootstrap snippet:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initFactory()
{
$factory = new My_Factory($this);
return $factory;
}
}
Then the factory:
class My_Factory
{
protected $_registry;
protected $_bootstrap;
public function __constructor($bootstrap)
{
$this->_bootstrap = $bootstrap;
}
public function getDbAdapter()
{
if (!isset($this->_registry['dbAdapter']){
$this->_bootstrap->bootstrap('db'); // probably using app resource
$this->_registry['dbAdapter'] = $This->_bootstrap->getResource('db');
}
return $this->_registry['dbAdapter'];
}
public function getArticleService()
{
if (!isset($this->_registry['articleService']){
$dbAdapter = $this->getDbAdapter();
$this->_registry['articleService'] = new My_ArticleService($dbAdapter);
}
return $this->_registry['articleService'];
}
public function getTwitterService()
{
if (!isset($this->_registry['twitterService']){
$options = $this->_bootstrap->getOptions();
$user = $options['twitter']['user'];
$pass = $options['twitter']['pass'];
$this->_registry['twitterService'] = new My_TwitterService($user, $pass);
}
return $this->_registry['twitterService'];
}
}
Then in a controller, you could grab an ArticleService instance:
class SomeController extends Zend_Controller_Action
{
protected $_factory;
public function init()
{
$this->_factory = $this->getInvokeArg('bootstrap')->getResource('factory');
}
public function someAction()
{
$articleService = $this->_factory->getArticleService();
$this->view->articles = $articleService->getRecentArticles(5); // for example
}
}
The upshot here is that each service explicitly identifies the collaborators it needs and the factory is a single place that takes care of creating/injecting all those collaborators.
Finally, I confess that I am just spitballing here. To me, this is essentially a rudimentary dependency injection container; in that sense, using a fully-featured DIC - perhaps the Symfony DIC or the new Zend\Di package in ZF2 - might be better. But after many months of struggling with all the best-practice recommendations to inject your dependencies, this is what I have come up with. If it's goofy or just plain wrong, please (please!) straighten me out. ;-)
I wrote a plugin that needs to set a property on the controller that's currently being dispatched. For example, if my plugin is:
class Application_Plugin_Foo extends Zend_Controller_Plugin_Abstract
{
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
// Get an instance of the current controller and inject the $foo property
// ???->foo = 'foo';
}
}
I want to be able to do this:
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->foo = $this->foo;
}
}
}
Any help is greatly appreciated!
The action controller is not directly accessible directly from a front-controller plugin. It's the dispatcher that instantiates the controller object and he doesn't appear to save it anywhere accessible.
However, the controller is accessible from any registered action helpers. Since action helpers have a preDispatch hook, you could do your injection there.
So, in library/My/Controller/Helper/Inject.php:
class My_Controller_Helper_Inject extends Zend_Controller_Action_Helper_Abstract
{
public function preDispatch()
{
$controller = $this->getActionController();
$controller->myParamName = 'My param value';
}
}
Then register an instance of the helper in application/Bootstrap.php:
protected function _initControllerInject()
{
Zend_Controller_Action_HelperBroker::addHelper(
new My_Controller_Helper_Inject()
);
}
And, as always, be sure to include My_ as an autoloader namespace in configs/application.ini:
autoloaderNamespaces[] = "My_"
Then, in the controller, access the value directly as a public member variable:
public function myAction()
{
var_dump($this->myParamName);
}
One thing to note: Since the helper uses the preDispatch() hook, I believe it will get called on every action, even an internal forward().
Browsing through the API, I didn't find a way to reach the controller directly (I'm guessing this loop is performed before the controller exists). What I could find is almost as easy to access, albeit with a bit different syntax.
Via request params
class Application_Plugin_Foo extends Zend_Controller_Plugin_Abstract
{
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$yourParam = 'your value';
if($request->getParam('yourParam')) {
// decide if you want to overwrite it, the following assumes that you do not care
$request->setParam('yourParam', $yourParam);
}
}
}
And in a Zend_Controller_Action::xxxAction():
$this->getParam('yourParam');
Via Zend_Controller_Action_Helper_Abstract
There's another way mentioned in MWOP's blog, but it takes the form of an action helper instead: A Simple Resource Injector for ZF Action Controllers. His example would let you access any variable in Zend_Controller_Action as $this->yourParam.