How can I override Laravel Facade methods? - laravel

I want to override the Laravels' Mail's classes facade method send (just intercept it forcing some checks and then if it passes triggering parent::send())
What is the best way to do this?

A Facade doesn't work like that. It's essentially kind of like a wrapper class that calls the underlying class that it represents.
The Mail facade doesn't actually have a send method. When you do Mail::send(), under the hood, the "facade accessor" is used to reference an instance of the Illuminate\Mail\Mailer class bound in the IoC container. It's on that object the send method is called.
The way in which you can achieve what you're after is actually a little bit trickier than it seems. What you can do is:
Write your own implementation of Mailer, extending Illuminate\Mail\Mailer, in which you can override the send method, implement your checks and call parent::send().
Write your own service provider (Extending Illuminate\Mail\MailServiceProvider), in particular re-implement the register method. It should create an instance of your own Mailer in place of Laravel's own. (You can copy most of the code from Laravel's register method).
Now, in your config/app.php file, in the providers array, replace Illuminate\Mail\MailServiceProvider::class, with your own provider.
That should let you hook into Laravel's Mail functionality.
For more information, you can take a look at the following question/answer which achieves a similar thing. It extends the Mail functionality to add a new transport driver, but it takes a similar approach in that it provides its own Mailer implementation and service provider.
Add a new transport driver to Laravel's Mailer
app/MyMailer/Mailer.php
<?php
namespace App\MyMailer;
class Mailer extends \Illuminate\Mail\Mailer
{
public function send($view, array $data = [], $callback = null)
{
// Do your checks
return parent::send($view, $data, $callback);
}
}
app/MyMailer/MailServiceProvider.php (Most of the code copied from Laravel's MailServiceProvider class)
<?php
namespace App\MyMailer;
class MailServiceProvider extends \Illuminate\Mail\MailServiceProvider
{
public function register()
{
$this->registerSwiftMailer();
$this->app->singleton('mailer', function ($app) {
// This is YOUR mailer - notice there are no `use`s at the top which
// Looks for a Mailer class in this namespace
$mailer = new Mailer(
$app['view'], $app['swift.mailer'], $app['events']
);
$this->setMailerDependencies($mailer, $app);
$from = $app['config']['mail.from'];
if (is_array($from) && isset($from['address'])) {
$mailer->alwaysFrom($from['address'], $from['name']);
}
$to = $app['config']['mail.to'];
if (is_array($to) && isset($to['address'])) {
$mailer->alwaysTo($to['address'], $to['name']);
}
return $mailer;
});
}
}
config/app.php (In the providers array)
//...
// Illuminate\Mail\MailServiceProvider::class,
App\MyMailer\MailServiceProvider::class,
//...

Related

Facades vs. Classes with static methods in Laravel

I was looking around the Laravel framework and some of their products and I noticed that Cashier is using the Casheir class with static methods compared to Socialite, which is used as a facade.
What are the benefits/downsides of building it one or the other way, or is there none at all?
I would like to build something myself, but I don't want to start building it as a class with static methods if building it as a facade is a better solution.
When you may need multiple implementations, a single interface can be defined through facade to simplify the code
Building it as a class with static methods:
When you have multiple classes you have to do something like this:
CashierOne::method, CashierTwo::method ....
Used as a facade:
According to what you bind to the container to switch the implementation
You only need to call through an interface:
// Define a Cashier Facade
class Cashier extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'cashier';
}
}
// In CashServiceProvider
$this->app->singleton('cashier', function ($app) {
return new CashierManager ($app);
});
// In CashierManager
public function gateway($name = null)
{
// get cashier implementation by name
}
public function __call($method, $parameters)
{
return $this->gateway()->$method(...$parameters);
}
// In Controller
Cashier::method
In addition, the facade is easier to test, check:
https://laravel.com/docs/5.8/facades#how-facades-work

Is there a way to send params to an observer?

Is there a way to send parameters to an Observer in Eloquent ORM?
Based on laravel's documentation:
User::observe(UserObserver::class);
observe method receive a class, not an instance of an object. So I cant do something like:
$observer = new MyComplexUserObserver($serviceA, $serviceB)
User::observe($observer);
So, in my code I can do something like:
class MyComplexUserObserver
{
private $serviceA;
private $serviceB;
public function __constructor($serviceA, $serviceB){
$this->serviceA = $serviceA;
$this->serviceB = $serviceB;
}
public function created(User $user)
{
//Use parameters and services here, for example:
$this->serviceA->sendEmail($user);
}
}
Is there a way to pass parameters or services to a model observer?
Im not using laravel directly, but i'm using eloquent (illuminate/database and illuminate/events)
Im not trying to send additional parameters to an explicit event like in: Laravel Observers - Any way to pass additional arguments?, i'm trying to construct an observer with additional parameters.
FULL SOLUTION:
Thank you to #martin-henriksen.
use Illuminate\Container\Container as IlluminateContainer;
$illuminateContainer = new IlluminateContainer();
$illuminateContainer->bind(UserObserver::class, function () use ($container) {
//$container is my project container
return new UserObserver($container->serviceA, $container->serviceB);
});
$dispatcher = new Dispatcher($illuminateContainer);
Model::setEventDispatcher($dispatcher); //Set eventDispatcher for all models (All models extends this base model)
User::observe(UserObserver::class);
In the Illuminate events there is the line, this indicates on event subscription it utilities the container. This mean we can use this to our advantage, i'm not super familiar with non Laravel bootstrapped applications. But where ever your app is defined, you will bind your class to your own class.
$container = new Container();
$container->bind(MyComplexUserObserver::class, function ($app) {
return new MyComplexUserObserver($serviceA, $serviceB, $theAnswerToLife);
});
$dispatcher = new Dispatcher($container);
This will result in, next time your application resolves your class, it will use this version of it and therefor you can setup your class as you intend.
Edit: an example how you can utilize the Laravel container, to utilize the bind functionality.

Set Mailable header via a trait?

I'm creating a Laravel package that would benefit from the use of emails. When a user would use my package they would want to email a file created by the package, but also set some custom headers for the email.
In an ideal solution, I would like to have a trait that the developer could simply use on their mailable class and it would automatically set the header for that email without any additional code. Is this something that is even possible via the use of a trait?
Some solutions have suggested adding headers to mailables by putting this in the build method:
$this->withSwiftMessage(function ($message) {
$headers = $message->getHeaders();
$headers->addTextHeader('mime', 'text/calendar');
});
But is there some way to have my own custom trait piggy-back on the build method of the Mailable that is using it WITHOUT having to write it in the Mailable class itself?
SOLUTION USING TRAITS
The only way to be able to do that with a trait would be defining the build method in your trait and make your user define another function instead of build such that you have the direct manipulation of the function actually used by Mailable class.
So your trait would be:
trait IsMailable {
public function build()
{
$this->withSwiftMessage(function ($message) {
$headers = $message->getHeaders();
$headers->addTextHeader('mime', 'text/calendar');
});
if(!method_exists($this, 'buildMail')) throw \Exception('buildMail is not defined!');
return $this->buildMail();
}
}
So your user will have to define the method buildMail instead of build.
OPTIMAL SOLUTION
The optimal solution, IMHO, would be extending the class Illuminate\Mail\Mailable redefining the method send and making the end user implement this newly defined class instead of Illuminate\Mail\Mailable.
So your class would be:
class Mailable extends \Illuminate\Mail\Mailable {
/**
* Send the message using the given mailer.
*
* #param \Illuminate\Contracts\Mail\Mailer $mailer
* #return void
*/
public function send(MailerContract $mailer)
{
$this->withSwiftMessage(function ($message) {
$headers = $message->getHeaders();
$headers->addTextHeader('mime', 'text/calendar');
});
parent::send($mailer);
}
}
Doing that your user can use the build method as it would do using the standard Illuminate\Mail\Mailable class but the end result would be that your class is piggybacking the additional information that you actually need.

Why won't this Laravel 5.4 service provider register?

I am trying to do a hello world service provider with the new Laravel 5.4.
I have created the following service provider file:
//File: app/TestProvider/TestServiceProvider.php
namespace App\TestProvider;
use Illuminate\Support\ServiceProvider;
class TestServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
$this->app->bind('Test', function ($app) {
return new Test();
});
}
}
I have created a simple class under the same namespace:
//File: app/TestProvider/Test.php
namespace App\TestProvider;
class Test
{
/**
* Register bindings in the container.
*
* #return void
*/
public function helloWorld()
{
echo "hello world";
}
}
The problem is, this is not registering. The register method is executing as when I put a breaker before the 'bind' method, it executes:
public function register()
{
dd("BREAKER");
$this->app->bind('Test', function ($app) {
return new Test();
});
}
So this outputs "BREAKER" as expected. However if I put the breaker in the closure, nothing happens which suggests for some reason, that 'bind' method isn't being executed??
Any ideas?
EDIT:
Just some further info: I know that the Test class is registered and in the correct namespace as I can do:
dd(new Test());
in the registration method, and it outputs the resource id as expected.
Explanation
The closure provided only runs when the binding is being resolved. That's why it's a closure, it can be saved in the service container and resolved at any time while the program runs.
Solution
To see the resolved binding, create a controller and resolve the class in that controller:
// File: app/Http/Controllers/TestController.php
namespace App\Http\Controllers;
// This isn't the best way, but it works. See the best way below
class TestController extends Controller {
public function index()
{
return \App::make('Test')->helloWorld();
}
}
Of course, don't forget to register the route:
// File: routes/web.php
Route::get('/', 'TestController#index');
The binding will resolve when you hit the homepage.
However, as I said, it's not the best way, so here I prepared a better way. Change the way you register the binding:
// File: app/Providers/TestProvider.php
namespace App\TestProvider;
use Illuminate\Support\ServiceProvider;
use App\TestProvider\Test;
// Better way
class TestServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
// Note: we bind the exact complete class name!
$this->app->bind(Test::class, function ($app) {
return new Test();
});
}
}
After this change the controller so that it looks like this:
namespace App\Http\Controllers;
use App\TestProvider\Test;
class TestController extends Controller {
/**
* #var Test $test
*/
private $test;
// Let Laravel resolve the dependency on constructing the class
public function __construct(Test $test)
{
$this->test = $test;
}
public function index()
{
return $this->test->helloWorld();
}
}
You will see that the exact same thing happens, but it looks more elegant and avoids conflicts.
Details
Laravel gives only a high level overview of the service container, which doesn't help to learn how it works on the inside. The best way to see that is to go down the call stack.
When you do that, you find that Laravel registers every class in the project in the service container. That means that whether you create a service provider or not, the class will be in the container. How exactly?
When you run php artisan optimize, Laravel creates files that have array with all the classes of the project. When you run the app, after registering everything from the service providers, Laravel registers the rest of the classes from that file.
That means that in your case, if you don't specifically register the Test class, it will still be resolvable. Basically, you only need to register classes that need some specific instructions to be resolved.
So how does Laravel resolve the dependencies?
When you run \App::make(Test::class) or inject dependency via type hinting in the constructor (the "better way" from my solution), Laravel looks for that dependency among the bindings.
When it finds the dependency, it resolves either the closure associated to it or the constructor of the class directly.
When it resolves the constructor directly, it looks for type hints among the constructor parameters and recursively resolves all of them until there's nothing else to resolve.
After that it returns the resolved class.
Of course, bear in mind that for Laravel to analyze the contructor of a class, it needs to be resolved via the service container in the first place. You can't just call $test = new Test(); and expect Laravel to do all the magic :)
Conclusion
This is a rather quick overview of Laravel's service container. The best way for you to learn it is, of course, studying the sources for yourself. It's truly elegant and it uses PHP's functionality to the fullest.
I really hope this shed some light on the service container for you and can help you in the future :)
The closure passed to the bind() method is not executed until you actually attempt to resolve the alias you are binding.
So, if you dd('breaker') inside the closure, this won't actually get executed until Test is resolved (whatever your preferred resolution method is):
Service provider:
// bind the closure to the 'Test' alias
public function register()
{
$this->app->bind('Test', function ($app) {
dd("BREAKER");
return new Test();
});
}
Code that resolve Test alias:
// different ways of resolving the alias out of the container.
// any of these will execute the bound closure.
$test = resolve('Test');
$test = app('Test');
$test = app()->make('Test');
$test = \App::make('Test');
try:
$this->app->bind(Test::class, function ($app) {
return new Test();
});

How does laravel 5 know which contract implementation I want to use?

I'm kind of confused of how to use contracts. I think that's because I haven't used unit-testing so that it's not obvious for me how contracts work.
Let's have look at this code:
use Illuminate\Contracts\Auth\Guard;
...
public function __construct(Guard $auth)
{
$this->auth = $auth;
$this->middleware('guest', ['except' => 'getLogout']);
}
public function postRegister(RegisterRequest $request)
{
// Registration form is valid, create user...
$this->auth->login($user);
return redirect('/');
}
So how do I know which class implements login method of contract in this line: $this->auth->login($user) ? And how can I change the class if I want to use my own?
In laravel 4 I wrote Auth::user() as an example and I used it everywhere in any controller and it worked. Now I should inject a contract inside a controller method and use it like $auth->user?
Also, If I get it right, contracts are used for making an abstraction. Okay, so, if I want to build a new interface for my own class and then have multiple classes that implement my interface, where should I write the code? I can't think of an example but lets imagine I need to implement an interface for enabling/disabling a lamp, and I have two methods like on() and off() and I have multiple ways to do that. Do I need to create new contract for that?
I hope I can make this a bit clearer for you...
Ad.1. You can check default binding at /vendor/laravel/framework/src/Illuminate/Foundation/Application.php (method registerCoreContainerAliases around line 792). If you want to create your own class or extend existing I recommend looking at How to extend Laravel's Auth Guard class? or http://laravel.com/docs/master/extending (this one is more about Laravel 4.x but might give you an idea).
Ad.2. Actually you can still use Auth::user() but I inject a contract in constructor or a method and call it like $this->auth->user or $auth->user.
Ad.3. I have a /app/Repositories folder where I put my interfaces and implementations, so to follow your example I would create subfolder Lamp and I would create LampInterface with on() and off() methods, then I would create something like Lamp.php that implements LampInterface. Next I would create a service provider in /app/Providers, like LampServiceProvider.php with binding:
namespace Apps\Providers;
use Illuminate\Support\ServiceProvider;
class LampServiceProvider extends ServiceProvider {
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->singleton(
'App\Repositories\Lamp\LampInterface',
'App\Repositories\Lamp\Lamp'
);
}
}
After that I would register new service provider in /app/config/app.php and finally I can inject my interface like:
public function switchLampOn(App\Repository\Lamp\LampInterface $lamp)
{
$lamp->on();
}

Resources