Pass class by config using ::class and retrieve in Laravel - php

I have a config file with such array:
'ppr' => [
'validate' => TestRequest::class
];
Now, I want to retrive this class in other part of the system and use it to validate form (outside of the controller).
While using config('main.ppr.validate') all I receive is namespaced name of the class instead of the class object.
As I already accepted that it won't be that easy to just use reuqests as in controllers, I still do wonder how to pass a class by config.
While passing eloquent models it works like a charm (or i.e. config arrays with middlewares etc.), so I suppose there is some magic binding to the IoC to achive that, is it true?
My question is, how to use class passed as in example above without initializing it like:
$obj = new $className;

Laravel (and many other applications) use Dependency Injection to achieve this magic -- your words, not mine! :D
It seems that the Service Container is what handles this in Laravel and should be of help to you.
Directly from the Laravel docs (linked above):
Within a service provider, you always have access to the container via the $this->app property. We can register a binding using the bind method, passing the class or interface name that we wish to register along with a Closure that returns an instance of the class:
$this->app->bind('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
Also:
You may use the make method to resolve a class instance out of the container. The make method accepts the name of the class or interface you wish to resolve:
$api = $this->app->make('HelpSpot\API');
And:
If some of your class' dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the makeWith method:
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
IMHO, I would look up where/how this is implemented in the native Laravel code (usually the Illuminate vendor) and see how it is used / meant to be implemented.
Furthermore, ClassName::class will return the namespace + class of that class. This is why you only see the class name and are not actually receiving an object/instance of that class.
I'm not sure what/where/why/how you're implementing your class and why you need this functionality somewhere that it doesn't already exist. Laravel is pretty good about already having things set up where you need them, so think twice before breaking out of the box and make sure there isn't a default solution for your situation!

Related

Create class instances like Laravel

I was looking at some Laravel code for some new ideas for my framework, and I found they declare their class instances like:
Request $request;
Which then acts as a Request class reference, I also noticed that there's a namespace being added in the Laravel code.
I looked all around the PHP Manual, re-read the OOP Manual as well as re-read the namespace section as well, some code I've tried:
// Includes a class called PSM
PSM $psm;
$psm->version();
I understand that'd be under "alternate syntax" structure, and also couldn't find anything under that name either.
I realized this was similar to C# Syntax, whereas when declaring variables or class instances, you give them a specific data-type which with instances is their own class-name. (Might be the wrong definition, the main thing to take from that was the C# similarities of this syntax)
As it might be confusing, I'm talking about the creation of the $request instance from simply typing:
Request $request;
Which then brings the class instance into the scope of the Controller.
You're talking about IoC. You can bind any class using Laravel container and then use this syntax to resolve this class:
function index(Request $request)
The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.

Laravel 5.1 Documentation for Mail - confusing?

I am still new to Laravel 5.1, but I found the documentation very strange and confusing.
For example - according to the Laravel docs I can use send() method from the Mail facade to send email.
So far, so good. When I go to Laravel API and find Illuminate Support Facades Mail such method doesn't exist? https://laravel.com/api/5.1/Illuminate/Support/Facades/Mail.html
How can I understand what parameters this method takes and what it is returning on success/failure?
That's because it is using the Facade pattern.
In your app.php config file there is a section called 'aliases'. There is a line in that section: 'Mail' => Illuminate\Support\Facades\Mail::class, which points to the Facade, which returns the key of the bind in the service container (IoC) which returns the class/object to work with.
So you need to find the place where the bind is created. Binds are created by the methods App::bind('foo', .. ), App::singleton('foo', .. )or App::instance('foo', .. ).
I search for 'mailer' and found the file lluminate\Mail\MailServiceProvider where the bind is created:
$this->app->singleton('mailer', function($app) {
...
// this is the class resolved by the IoC.
$mailer = new Mailer(
$app['view'], $app['swift.mailer'], $app['events']
);
...
return $mailer;
});
As you can see, the class \Illuminate\Mail\Mailer is returned in the service provider and that is the class used when you use the Facade called Mail.
Quick way to discover the class behind the Facade:
You can also find the name of the class quickly by dumping the class name: dd( get_class( Mail::getFacadeRoot() ) );
More information
More information about the service container: Click!
More information about Facades in Laravel 5: Click!
More information about the Facade pattern: Click!
The Facade classes are basically helper classes to provide quick, easy access to the real classes that do the work. There's plenty of debate on the merits of facades, but that is not for this question.
If you call the getFacadeRoot() method on the facade, it will give you the instance of the object to which the facade points (e.g. Mail::getFacadeRoot() == \Illuminate\Mail\Mailer).
Now that you know the actual object being used, you can look up the methods on that object. Any methods you call on the Facade are passed through to the object that is returned by getFacadeRoot(). So, when you are calling Mail::send(), you are actually calling \Illuminate\Mail\Mailer::send() (though, non statically).

Laravel 5.1 service container: Binding using something other than the class name?

Bit of a long winded question, but here goes. I'm a little confused regarding some of the documentation for the service container in Laravel 5.1. I'll explain my current understanding of the container first, and then explain where my confusion has arisen.
So, I'm fairly sure I understand the process of registering a binding with the service container. To quote the documentation, you register a binding using the bind method, passing the class or interface name that we wish to register along with a Closure that returns an instance of the class:
$this->app->bind('HelpSpot\API', function ($app) {
return new HelpSpot\API($app['HttpClient']);
});
Now, in the Laravel 5.0 docs, that's actually stated slightly differently:
A Closure resolver is registered in the container with a key (typically the class name) and a Closure that returns some value.
So, in Laravel 5.0, it seems you were able to bind some class, say FooBar to a key that, while recommended to be the class name, could be something different, so for example:
$this->app->bind('myfoobarclass', function($app) {
return new FooBar($app['SomethingElse']);
});
And then later you could resolve the class using:
$fooBar = $this->app->make('myfoobarclass');
However, this would remove your ability ro resolve that class using type hinting, which I'm guessing is why the docs for 5.1 states specifically to use the class name. However, in the section on Facades (http://laravel.com/docs/5.1/facades#facade-class-reference), they list the facades along with their "service container binding key", which is different to their class name. Does this mean you can't resolve these facades using type hinting? Why would they register their bindings as something other than their class name? Or is this documentation simply out of date?
If anyone could shed some light on the reasons behind this inconsistency that would be amazing, thanks in advance.
You normally bind implementations to interfaces. So instead of adding something to the service container by the name of the class, you would instead use the name of an interface it implements instead:
$this->app->bind('App\HttpClientInterface', function ($app) {
return new \Guzzle\HttpClient;
});
You can then type-hint the HttpClientInterface interface in your application, and instead get the bound GuzzleHttpClient instance. This allows you to swap your implementations out in one place without having to re-write your application code.
You’re not restricted to using fully-qualified class/interface names, you can use arbitrary strings as key names if you wish:
$this->app->bind('http.client', function () {
return new \Guzzle\HttpClient;
});
But you can’t type-hint on these; this is where you would use the app()->make('http.client') method instead.
In regards to the Façade Class Reference section of the Laravel documentation, there are two ways of resolving the services in your app.
You can type-hint on the value in the Class column, i.e. Illuminate\Contracts\Hashing\Hasher
Alternatively, you can use what’s in the Service Container Binding column with the app()->make('hash') method. These are just “aliases” for the class name.
Hope this helps!

How to mock an Eloquent Model using Mockery?

I'm trying this:
$this->dsMock = Mockery::mock('Eloquent', 'API\V1\DataSet');
$this->app->instance('API\V1\DataSet', $this->dsMock);
$this->dsMock->shouldReceive('isLocalData')->once()->andReturn(true);
Then, inside the class under test:
$test = DataSet::isLocalData($dataSetId);
However, the DataSet class is not being mocked. It's still trying to access the database. Why?
The likely problem is Laravel's unfortunate over use of Façade's (which are also factories). If DataSet was already instantiated using the Façade, it will keep returning the same class, you won't get a mocked version.
I can't remember off hand if you can instantiate your class without using the Façade in Laravel. You have to remember that when you call DataSet statically in the application, you're actually not referencing API\V1\DataSet but something else that manages it.

Adapters and Dependency Injection

I am currently building an MVC application in PHP (not using any frameworks). I am using yadif (https://github.com/beberlei/yadif) for dependency injection.
I would like to build a login module. It should be able to use adapters, for example one might be able to set that logins are authenticated using the MySql database or some LDAP directory. This setting would be done in the admin area and is stored in a database.
I imagine that I would have an abstract adapter:
<?php
abstract AbstractLoginAdapter{
abstract function login($username, $pass){}
}
I would then just implement adapters like so:
<?php
MySQLLoginAdapter extends AbstractLoginAdapter{
public function login($username, $pass){
//do stuff
}
}
That's all nice and well, but how do I create an instance of the adapter? Usually, dependencies would be injected using yadif via the constructor:
<?php
class loginController{
private $_adapter;
public function __construct(AbstractLoginAdapter $adapter){
$this->_adapter = $adapter;
}
}
However, since I don't know which concrete adapter will be injected, I can't set that in a configuration before hand. Yadif allows me to create a configuration which I then need to pass to the container:
$builder = new Yadif_Builder();
$builder->bind("loginController")
->to("loginController")
->args($SelectedLoginAdapter);
Since the application uses a front controller, a DI container is created there. It then creates a routing object etc.
In light of this, should I pass a reference of that container down to the loginController object, and then use that container to instantiate my adapter?
Or should I instantiate a new container within my loginController object and then just load in an instance of the adapter?
I would do the first: pass a reference down to your controller. You'll want to use a single Dependency Injector Container (DIC) in your application. You don't want to create a new DIC whenever you need access to it. That would lead to duplication of objects stored in the DIC.
I know this is how Symfony 2 does it. All controllers (and many other classes) implement the ContainerAware interface. That interface has a single method setContainer() that is used to pass down a reference to the DIC.
I don't know about your specific DI tool but from a DI point of view you would be specifying which type to use. The container itself is responsible for instantiating a new instance (and possibly of all the dependencies of that type as well) of the configured type.
The benefit of DI in your example would be that you could deploy exactly the same code with a different configuration with 1 installation using LDAP and the other using MySQL authentication.
Refactor type hinting ("AbstractLoginAdapter") to ("MySQLLoginAdapter").
If you call abstract class method in the new __CLASS__ // Fatal Error.

Categories