Laravel 5.1 Documentation for Mail - confusing? - php

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).

Related

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

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!

How to provide a mock entity manager to test a service class?

I have a service class (OutletScraper) in my Symfony (3.4) application. The class uses Entity Manager and a Geocoding service provided by the Bazinga Geocoder bundle. I've configured both successfully so that I am able to call them from within my service class. Whenever I need the service, I am calling it from the container so the entity manager and geocoder bundle is injected into it already.
When testing, I understand that I can mock the entity manager and then provide this to my test class. As I am accessing the service class from the container, how do I override what gets passed to the constructor? ie so that I can provide the mock entity manager instead of it being injected with the real one. I tried to instantiate an object of the service class manually:
$outletScraper = new OutletScraper(new Provider(), $this->createMock(EntityManagerInterface::class));
However I get the following error when doing so:
Error: Cannot instantiate interface Geocoder\Provider\Provider
How can I instantiate this class correctly? Do I need to call it from the service container (its set to private)? Appreciate any help.
Your mock of the entity manager works fine, the problem is the geocoder provider. Just like EntityManagerInterface, Geocoder\Provider\Provider is also an interface. The library maintainers just chose to omit the suffix.
That means you can't just create it, but instead have to pass a concrete class implementing the interface, like Geocoder\Provider\GoogleMaps\GoogleMaps, if you actually want to do the geocoding call or mock the Provider as well.
If you want to check if your configured geocoding provider works you can write a functional test using Symfony's WebTestCase, that looks roughly something like this:
<?php
namespace AppBundle\Tests\Scraper;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class OutletScraperTest extends WebTestCase
{
public function testFindsLocation()
{
// This will instantiate your Symfony application in the test environment
$client = static::createClient();
$container = $client->getContainer();
// Replace the class name with the service id, if you use artificial service ids like "app.outlet_scraper"
$scraper = $container->get(OutletScraper::class);
// Call whatever method you want to test on your outlet scraper
$result = $scraper->someMethod();
// Assert result matches expectations
$this->assertEquals(..., $result);
}
}
Be careful though, that this will use the OutletScraper you configured for your application, with both actual dependencies. So your geocode provider will make an actual call to whatever provider you use, which might use up requests, which might be limited. Also you will use the real Doctrine EntityManager, meaning whatever you write to the database will actually be written. Especially for the database you should therefore create a separate test database and configure it in your app/config/config_test.yaml.
My recommendation would be that if you are trying to test this service alone, you should also mock the Geocoder\Provider\Provider. Otherwise, your test assertions could bring up different results depending on when you run them.
If you are going to use this mock a lot, you could even create your own testing class that implements that interface, so that you can prepare the different outputs yourself on your test environment.

How to mock doctrine service for controller action when doctrine is used elsewhere

I have a controller action that contains some code like the following:
$repository = $this->get("doctrine")->getRepository(User::class);
$user = $repository->findOneBy(array('username' => $request->request->get("username")));
I wanted to mock the repository. At first I wasn't sure how to do that, but then I found this SO post: Testing Controllers in Symfony2 with Doctrine
From the answer there, I surmised that I should create a mock of the doctrine service AND the repository object, and tell the mockbuilder that the repository object returns the entity I want to test. Then I should replace the doctrine service using the following line of code:
$client->getContainer()->set("doctrine", $doctrineMockObject);
and then make the request:
$client->request("POST", "/checkUsername");
The problem with this is that there is a twig template that actually calls a separate controller action, and that action uses doctrine as well. So that causes the application to break since it is using the mock doctrine object I injected into the container.
Is there any way to only use the mock doctrine service for the action that I am testing? Otherwise, is there any alternative method to do what I want to do? I am out of ideas.
As 'alternative method' you can stop using Container as Service Locator and instead of injecting it everywhere inject only services that you need into controllers/other services.

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!

Laravel Package Development simple function does not work

Sorry for the bad title, but I had no idea to give a good title for my problem.
I created a package with the workbench. And now I have a package. Let's say strernd/mypkg
Inside the workbench directory Strernd/Mypkg I've got following code:
http://laravel.io/bin/oNzoa
Why does Mypkg::test()work and $mypkg = new Mypkg(); $mypkg->test(); not? (Inside my app)
The error is
Call to undefined method Strernd\Mypkg\Facades\Mypkg::test()Call to undefined method Strernd\Mypkg\Facades\Mypkg::test()
I think I'm not understanding some basic principles of PHP here. I'm more like a copy&paste / try&error "developer", but it works out well in most cases.
When using it the 'static' way, Laravel is accessing the facade you wrote and instantiating the underlying class, then calling the method on that instance. When you are calling the test method with new Mypkg, you are actually instantiating the facade class, and not the underlying class. That facade class does not have a test method on it, hence the error.
Try it like this:
use Strernd\Mypkg as MypkgActual;
$mypkg = new MypkgActual;
$mypkg->test();
I believe that, because your facade and actual class implementation have the same exact class name, you're running in to a pseudo-class name conflict with Laravel's facade system winning out. You can also try changing the name of your facade to MypkgFacade.

Categories