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.
Related
Service containers / providers are probably much simpler concepts than I imagine, but after several hours of reading I still don't get it, entirely.
I have created a simple DateFormat class within app/Library. After creating an alias for it inside \config\app.php I can use it right away in any controllers or blade templates.
<?php namespace App\Library;
class DateFormat {
public static function getDate($timestamp){
// processing the timestamp
}
}
Did I just create a Service Container? If yes, do I need to create a Service Provider as well? Where come bindings into the picture?
I would really appreciate some light on the subject.
Thanks
No. What you created is simply an alias to your class. Services Providers are a way of binding a specific class, and often are used in conjuction with a Facade.
An alias is simply a convenient way to use a class without having to import the entire namespaced class every time.
For example, if you have a class \My\Very\Long\Class\Adapter, you could alias this in config/app.php:
// config/app.php
<?php
'aliases' => [
// a bunch of aliases
'MyAdapter' => My\Very\Long\Class\Adapter::class,
]
And now you can just do:
<?php
new MyAdapter();
...
instead of:
<?php
use My\Very\Long\Class\Adapter;
...
new Adapter();
...
A Service Provider is often used when you want to resolve a dependency, most commonly through injection. This can be helpful when the class you want to resolve requires parameters to passed into the constructor or has a common setup every time. You can perform all that setup in the Provider.
Here's a scenario:
You have an API that you want to interact with. We'll call it SuperApi. The docs for SuperAPI say that to create an instance of the SuperApi class, you have to do something like:
<?php
// Some method (a controller or something)
public function index()
{
$superApi = new \SuperApi\Connector($key, $secret);
return $superApi->getCustomers();
}
Now, every time you want to create an instance of this, you'll have to do the same setup (or abstract it to some class, but the fact remains that you need to pass a $key and $secret to the constructor).
If you were to create an alias for this Connector class, maybe it would be:
// config/app.php
<?php
'aliases' => [
// a bunch of aliases
'SuperApi' => SuperApi\Connector::class,
]
So with that alias, you can now do this:
<?php
// Some method (a controller or something)
public function index()
{
$superApi = new SuperApi($key, $secret);
return $superApi->getCustomers();
}
But you see, that even with the alias, you still need to pass the $key and $secret.
This is where a Service Provider can help.
// app/Providers/SuperApiProvider.php
<?php
namespace App\Providers;
use SuperApi\Connector;
use Illuminate\Support\ServiceProvider;
class SuperApiProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
$this->app->bind('superApiConnector', function ($app) {
return new ApiConnector($app['config']->get('super-api.key'), $app['config']->get('super-api.secret'));
});
}
}
// app/Providers/SuperApi.php (the Facade)
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Facade;
class SuperApi extends Facade
{
protected static function getFacadeAccessor()
{
return 'superApiConnector';
}
}
// config/super-api.config
<?php
return [
'key' => env('SUPER_API_KEY'),
'secret' => env('SUPER_API_SECRET'),
];
// config/app.php
<?php
'providers' => [
// a bunch of providers
App\Providers\SuperApiProvider::class,
]
See that the string you bind to in the provider ('superApiConnector') is the same as what you return from the facade and the class name of the facade is how you'll actually call the binded class, in this case SuperApi.
Now, when you want to user the SuperApi\Connector class, you can do this:
<?php
// Some method (a controller or something)
public function index()
{
return SuperApi::getCustomers();
}
And as I said above, where a provider really comes in handy is when you want to inject it and have Laravel's IoC Container automatically resolve the injected class:
<?php
// Some method (a controller or something)
public function index(SuperApi $api)
{
return $api->getCustomers();
}
To be clear, you do NOT need a Service Provider to take advantage of dependency injection. As long as the class can be resolved by the application it can be injected. That means whatever arguments the constructor of the class you're injecting takes need to also be auto-resolvable.
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.
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.).
I'm trying to figure out how to add a method to a class in a Laravel package, so that all controllers and models that call that class can access the new method. How do I replace this class in the IoC?
This is the package in question, Angel CMS. The package is my creation, so I can modify it if we need to add aliases or anything to accomplish this.
Let's say I want to add a method to this class:
vendor/angel/core/src/models/PageModule.php
Okay, so I copy the class file to here:
app/models/PageModule.php
And then I modify the copied file, adding a namespace and the desired custom_function method:
<?php namespace MyModels;
use Eloquent;
class PageModule extends Eloquent {
protected $table = 'pages_modules';
public static function custom_function()
{
return 'It works!';
}
}
As you can see, I am using the MyModels namespace here.
Then, I run a composer dump-autoload.
Next, I open up my app/routes.php and register the binding and set up a test route:
App::bind('PageModule', function($app) {
return new \MyModels\PageModule;
});
Route::get('test-binding', function() {
return PageModule::custom_function();
});
But, when visiting the test route, I always receive the same error that the method is undefined.
What am I doing wrong here? Thank you in advance for any help.
To Clarify:
I am attempting to replace the class application-wide so that all other classes (controllers/models/etc.) that call PageModule will have access to the custom_function method. Thanks.
To be honest, I'm pretty new to all this IoC, dependency inversion/injection concept too. But I think I've gone through the same struggle before. What I would do, as much as my knowledge allows, is...
Add a constructor to src/controllers/admin/AdminPageController.php:
protected $pageModule;
public function __construct(PageModule $pageModule)
{
$this->pageModule = $pageModule;
}
Then where you did $module = new PageModule in the same file. You replace it with:
$module = $this->pageModule;
The two modifications above makes use of Laravel's IoC to allow injecting a different PageModule object into your controller, instead of strictly creating PageModule in your code.
Now at this point Laravel should know that when it constructs the AdminPageController, it should create a PageModule and inject into the controller for you.
Since your controller now expects a PageModule class, you can no longer do class PageModule extends Eloquent in your app anymore, because even though the name is the same, PHP does not think that it is! You'll need to extend it:
So let's rename your app/models/PageModule.php to app/models/CustomPageModule.php, and in the file change the class to:
class CustomPageModule extends \PageModule {
Up to this point, you also have a CustomPageModule class that is a child of your package's PageModule. All you need to do now is to let Laravel knows that if any controllers ask for PageModule, it should serve the controller with your MyModels\CustomPageModule instead.
So at the top of your app's routes.php file:
App::bind('PageModule', 'MyModels\CustomPageModule');
Your AdminPageController should now be using your CustomPageModule and can use whatever public methods that are in there!
I'm expecting to be editing this answer heavily since this will be quite a long discussion. My first try at answering above isn't the best code you can write, but I hope it takes the least amount of edit to the original code, and then we can work up from there.
Or fast track by reading up articles like http://culttt.com/2013/07/08/creating-flexible-controllers-in-laravel-4-using-repositories
You probably have a alias for the PageModule facade, you should override this alias using your class \MyModels\PageModule in your app/config/app.php file.
Be careful, it seems like you are overwriting the PageModule class instead of extending it. You should probably extend the parent class instead of Eloquent.
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.