So in trying to implement IoC, DI, etc. in Laravel 4, I've hit a wall. Either I'm misunderstanding something or doing something horribly wrong, not sure which...
So I have a class Person ("business class", not a model or library):
namespace Entities;
use Interfaces\Person as PersonInterface;
class Person implements PersonInterface {...}
a factory which has:
use Interfaces\Person;
...
App::singleton('user', function($app) {
...
$user_object = new Person();
...
});
and in the aliases array:
'Interfaces\Person' => 'Entities\Person'
Problem is that doesn't work because the Person class can't implement its interface because the interface is bound back to the Person class:
Entities\Person cannot implement Entities\Person - it is not an interface
I seem to be caught in a catch 22 of using IoC and interfaces in the application preventing the classes from actually instantiating.
Don't know if it's relevant, but putting
App::bind('Interfaces\Person','Entities\Person');
in the routes.php files doesn't seem to do anything (but putting it in the aliases array does). Surely I'm doing something wrong here. Any ideas?
Maybe I can help. To bind an interface to the IoC, you want to have an interface and an implementation of the interface. It looks like you have that step correct. You also want to create a service provider. More info on that here: http://laravel.com/docs/ioc#service-providers
Remove any bindings you have from the routes.php file. The service provider is what binds the route, and config/app.php registers it in the IoC as described more fully below.
Your service provider might look something like this:
File Name: ServiceProviders/PersonServiceProvider.php
<?php namespace ServiceProviders;
use Illuminate\Support\ServiceProvider;
use Entities\Person;
class PersonServiceProvider extends ServiceProvider {
/**
* Register the binding.
*
* #return void
*/
public function register()
{
$this->app->bind('Interfaces\Person', function()
{
return new Person();
});
}
}
Once the service provider is created, register it in the config/app.php file as follows:
'ServiceProviders\PersonServiceProvider',
Don't use the aliases. This is used for registering the aliases of facades, which is not what you're attempting to do here if I understand your question correctly.
Lastly, to follow the generally accepted Laravel naming conventions, I'd suggest naming the interface file "PersonInterface.php" and its interface "interface PersonInterface." Similarly, the implementation file might be called "EloquentPerson.php" and the "class EloquentPerson extends PersonInterface". This assumes you're using Eloquent. It's similar to what you have, but I think the class and interface names could make it more readable with those small tweaks.
Related
As for the title I've googled about two hours searching for a efficient answer and read repeatedly the official documentation, but without any step further, considering I'm relatively new to the framework. The doubt arise while searching for a correct way to share some code between controllers and i stumbled in service providers, so:
I've created say a MyCustomServiceProvider;
I've added it to the providers and aliases arrays within the app.php file;
finally I've created a custom helpers class and registered it like:
class MyCustomServiceProvider extends ServiceProvider
{
public function boot()
{
//
}
public function register()
{
$this->app->bind('App\Helpers\Commander', function(){
return new Commander();
});
}
}
So far, however, if I use that custom class within a controller I necessarily need to add the path to it through the use statement:
use App\Helpers\Commander;
otherwise I get a nice class not found exception and obviously my controller does not his job.
I suspect there's something which escapes to me on service providers! :-)
So far, however, if I use that custom class within a controller I
necessarily need to add the path to it through the use statement:
`use App\Helpers\Commander;`
otherwise I get a nice class not found
exception and obviously my controller does not his job.
Yes, that's how it works. If you don't want to use the full name, you can use a Facade instead.
Create the Facade class like this:
class Commander extends Facade
{
protected static function getFacadeAccessor() { return 'commander'; }
}
register the service:
$this->app->singleton('commander', function ($app) {
return new Commander();
});
add the alias to your config/app.php:
'aliases' => [
//...
'Commander' => Path\To\Facades\Commander::class,
//...
],
and use it like a Facade:
\Commander::doStuff();
On why your code still works, even when you remove the bind:
When you type-hint a parameter to a function, and Laravel does not know about the type you want (through binding), Laravel will do its best to create that class for you, if it is possible. So even though you didn't bind the class, Laravel will happily create a instance of that class for you. Where you actually need the binding is when you use interfaces. Usually, you'd not type-hint specific classes but a interface. But Laravel can not create a instance of an interface and pass it to you, so Laravel needs to know how it can construct a class which implements the interface you need. In this case, you'd bind the class (or the closure which creates the class) to the interface.
Good day!
The more I read, the more I get confused about this. What is the difference between a Facade and Aliases?
I have this Class:
/app/libraries/Project/Data.php
namespace PJ;
class Data {
// It is much like a data container, with static methods and properties for saving info
}
And the corresponding facade, so I can access by using just PJD:: .
According to some webpage around:
... Laravel Facades are proxies. They wrap around and call functions
on the underlying true implementation of the code. Further, in the
context of a Laravel application, these Facades are accessed by
assigning them to aliases. This use of the Dependency Injection
container allow you to reference something like
Illuminate\Support\Facades\Filesystem by simply calling File.
(http://ryantablada.com/post/proxies-service-locators-alias-facades-and-war)
But, I've also found and successfully tested that adding something like:
__app/config/app.php__
'aliases' => array(
//....,
'PJD' => 'PJ\Data',
),
I can also access my class the same way.
So, what's the difference?
Thanks
EDIT #01
I have created a class named Data in /app/libraries/Project/Data.php
namespace PJ;
class Data {
// It is much like a data container, with static methods and properties for saving info
}
I have a Facade Class for this Class Data /app/libraries/Project/DataFacade.php
use Illuminate\Support\Facades\Facade;
class PJD extends Facade {
protected static function getFacadeAccessor() {
return 'PJData';
}
}
And I have a Service Provider for them: /app/libraries/Project/DataServiceProvider.php
use Illuminate\Support\ServiceProvider;
class DataServiceProvider extends ServiceProvider {
public function register() {
$this->app->singleton('PJData', function() {
return new PJ\Data;
});
}
}
I also have added to /app/config/app.php:
'providers' => array(
// ....
'DataServiceProvider',
),
and in composer.json I've added a psr-4 line to direct PJ namespace to /app/libraries/Project
"psr-4": {
"PJ\\": "app/libraries/Project"
},
By doing all this, I can access my class from anywhere in the project just by PJD:: instead of PJ\Data::.
However, I've also noticed that just by adding to /app/config/app.php
'aliases' => array(
//....,
'PJD' => 'PJ\Data',
),
I get exactly the same result without all that facades and ServiceProviders. So, what's the point of one or another?
Thanks, and sorry for the large post.
Facade and Alias are two totally different concepts.
you can not access PJ\Data\ by PJD:: unless you have setup alias in the service provider while binding.
If you are accessing it, without defining it in config/app.php, then you have set it up in the service provider file itself.
Definition of alias,
used to indicate that a named person is also known or more familiar under another specified name.
It simply means you are giving a different name to the class so that it will be easier to call.
e.g.
if you have a class like this: Foo\Bar\AVeryLongNamespaceClassName\Data, you can just give an alias, (e.g. PJD) and access its methods and properties by this alias.
Note:
Unit testing is an important aspect of why facades work the way that they do. In fact, testability is the primary reason for facades to even exist.
This is the error that I keep receiving: Reflection Exception Class validator does not exist This is the code causing the problems:
use Illuminate\Support\ServiceProvider;
class DeskServiceProvider extends ServiceProvider
{
/**
* Register bindings
*
* #return void
*/
public function register()
{
$this->repositories();
$this->app->bind('Desk\Forms\MessageForm', function($app) {
$validator = $app->make('validator')->make([], []);
return new \Desk\Forms\MessageForm($validator);
});
}
}
I now know that I need to add a Validator class but I am not sure where or what to put in it. Thank you for all your help.
Your question is a little confusing, as is your code. If looks like you're trying to bind a service.
$this->app->bind('Desk\Forms\MessageForm'
However, instead of telling Laravel the service name you want to use to identify your service (like db, or message_form, etc.) you're passing it a class name (Desk\Forms\MessageForm).
Then, you're using the application's make factory to instantiate a validator object. It's not clear if you're trying to use make to instantiate an object from a class named Validator, or if you're trying to instantiate a service object from a service named validator. If the later, it doesn't look like a validator service exists in your application. If the former, it doesn't look like a class named Validator is defined anywhere Laravel can autoload from.
Regarding the next obvious question: Where can Laravel autoload from, you either want this Validator class in your composer package's src folder, named in a way that's PSR valid. If you're not using composer and this is a local application, the easiest thing to do is drop the file in
app/models/Validator.php
However, it's also not clear from your question if you're trying to use the Laravel built-in Validator service facade/object. A better question might yield a better answer. (possibly of interest, and a self link, I'm in the middle of writing a series of articles that explains the Laravel application container, which you may find useful.).
For the most part I get the idea behind a facade and how the details of instantiating the object are hidden behind the static calls.
So if we take a look at this straight forward example here: Example
We see some code like this for the facade, which at this point just sets up some sort of Alias. At this point it seems the facade still knows nothing about the Superuser class.
class SuperuserFacade extends Facade
{
protected static function getFacadeAccessor() {
return 'MyAlias';
}
}
The logic is glued by the service provider here it seems:
class SuperuserServiceProvider extends ServiceProvider
{
public function register() {
App::bind('MyAlias', function(){
return new Superuser;
});
}
}
But it just binds the class the facade alias MyAlias. Why bother with that facade class and two separate files, can't we just do all this logic right in the service provider? Or alternatively in the facade provider having it just return the Superuser class?
It seems like we have the facade not really doing anything and then another file telling that facade what to do. Why have these concerns been separated?
The facade class is a simple proxy -- it directs any calls to the facade class to the root class, which is retrieved from the IoC container via the facade accessor (in this case, MyAlias).
I call Superuser::whoami()
The Superuser facade goes "okay, I need to find the class that I'm a proxy for."
The facade calls getFacadeAccessor() to determine what IoC binding to retrieve and subsequently call.
The facade requests the MyAlias key from the IoC container. The container then returns an existing class if it has been built already, or it runs its bound closure which generates a new Superuser instance.
Now that the facade knows what class it's passing calls to, it forwards the whoami() method call to the newly-returned Superuser instance, which then returns whatever it is designed to.
The service provider's register() method simply registers a binding to the IoC container, to be retrieved later by whatever needs it. That's it. MyAlias is a simple string key used to look up that binding.
The facade allows you to use an IoC-bound class as if it were a static class.
I recommend reading other articles about the concept, because the article you linked is both inaccurate and not very informational about why things work. Here is a better article by Chris Fidao.
I would like to extend Laravels Router class (Illuminate\Routing\Router) to add a method I need a lot in my application.
But sadly I can't get this to work. I already extended other classes successfully so I really have no idea where my wrong thinking comes from.
Anyway, right into the code:
<?php
namespace MyApp\Extensions;
use Illuminate\Routing\Router as IlluminateRouter;
class Router extends IlluminateRouter
{
public function test()
{
$route = $this->getCurrentRoute();
return $route->getParameter('test');
}
}
So as you see I want to get the parameter set by {test} in routes.php with a simple call like:
Router::test();
Not sure how to go on now. Tried to bind it to the IOC-Container within my ServiceProvider in register() and boot() but I got no luck.
Whatever I try I get either a constructor error or something else.
All solutions I found are too old and the API has changed since then.
Please help me!
edit:
I already tried binding my own Router within register() and boot() (as said above) but it doesn't work.
Here is my code:
<?php
namespace MyApp;
use Illuminate\Support\ServiceProvider;
use MyApp\Extensions\Router;
class MyAppServiceProvider extends ServiceProvider {
public function register()
{
$this->app['router'] = $this->app->share(function($app)
{
return new Router(new Illuminate\Events\Dispatcher);
}
// Other bindings ...
}
}
When I try to use my Router now I have the problem that it needs an Dispatcher.
So I have to do:
$router = new Router(new Illuminate\Events\Dispatcher); // Else I get an exception :(
Also it simply does nothing, if I call:
$router->test();
:(
And if I call
dd($router->test());
I get NULL
Look at: app/config/app.php and in the aliases array. You will see Route is an alias for the illuminate router via a facade class.
If you look at the facade class in Support/Facades/Route.php of illuminate source, you will see that it uses $app['router'].
Unlike a lot of service providers in laravel, the router is hard coded and cannot be swapped out without a lot of work rewiring laravel or editing the vendor source (both are not a good idea). You can see its hardcoded by going to Illuminate / Foundation / Application.php and searching for RoutingServiceProvider.
However, there's no reason i can think of that would stop you overriding the router class in a service provider. So if you create a service provider for your custom router, which binds to $app['router'], that should replace the default router with your own router.
I wouldn't expect any issues to arise from this method, as the providers should be loaded before any routing is done. So overriding the router, should happen before laravel starts to use the router class, but i've not this before, so be prepared for a bit of debugging if it doesn't work straight away.
So I was asking in the official Laravel IRC and it seems like you simply can't extend Router in 4.1 anymore. At least that's all I got as a response in a pretty long dialogue.
It worked in Laravel 4.0, but now it doesn't. Oh well, maybe it will work in 4.2 again.
Other packages suffer from this as well: https://github.com/jasonlewis/enhanced-router/issues/16
Anyway, personally I'll stick with my extended Request then. It's not that much of a difference, just that Router would've been more dynamic and better fitting.
I'm using Laravel 4.2, and the router is really hard coded into the Application, but I extended it this way:
Edit bootstrap/start.php, change Illuminate\Foundation\Application for YourNamespace\Application.
Create a class named YourNamespace\Application and extend \Illuminate\Foundation\Application.
class Application extends \Illuminate\Foundation\Application {
/**
* Register the routing service provider.
*
* #return void
*/
protected function registerRoutingProvider()
{
$this->register(new RoutingServiceProvider($this));
}
}
Create a class named YourNamespace\RoutingServiceProvider and extend \Illuminate\Routing\RoutingServiceProvider.
class RoutingServiceProvider extends \Illuminate\Routing\RoutingServiceProvider {
protected function registerRouter()
{
$this->app['router'] = $this->app->share(function($app)
{
$router = new Router($app['events'], $app);
// If the current application environment is "testing", we will disable the
// routing filters, since they can be tested independently of the routes
// and just get in the way of our typical controller testing concerns.
if ($app['env'] == 'testing')
{
$router->disableFilters();
}
return $router;
});
}
}
Finally, create YourNamespace\Router extending \Illuminate\Routing\Router and you're done.
NOTE: Although you're not changing the name of the class, like Router and RoutingServiceProvider, it will work because of the namespace resolution that will point it to YourNamespace\Router and so on.