How to add headers to email in Laravel 5.1 - php

Is there a way to add default headers to all emails in Laravel 5.1? I want all emails to be sent with the following header:
x-mailgun-native-send: true

Laravel uses SwiftMailer for mail sending.
When you use Mail facade to send an email, you call send() method and define a callback:
\Mail::send('emails.reminder', ['user' => $user], function ($m) use ($user) {
$m->to($user->email, $user->name)->subject('Your Reminder!');
});
Callback receives $m variable that is an \Illuminate\Mail\Message object, that has getSwiftMessage() method that returns \Swift_Message object which you can use to set headers:
$swiftMessage = $m->getSwiftMessage();
$headers = $swiftMessage->getHeaders();
$headers->addTextHeader('x-mailgun-native-send', 'true');

I know this is an old post, but I'm passing by the same problem right now and I think this could be useful to others.
If you're using a structure exactly as shown in Laravel (6.x and 7.x), like this:
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->from('example#example.com')
->view('emails.orders.shipped');
}
you could add headers to the E-mail in the format below:
public function build()
{
return $this->from('example#example.com')
->view('emails.orders.shipped')
->withSwiftMessage(function ($message) {
$message->getHeaders()
->addTextHeader('Custom-Header', 'HeaderValue');
});
}
I hope this could be useful.
link to the current documentation:https://laravel.com/docs/7.x/mail#customizing-the-swiftmailer-message

Slight modification to #maxim-lanin's answer. You can use it like this, fluently.
\Mail::send('email.view', ['user' => $user], function ($message) use ($user) {
$message->to($user->email, $user->name)
->subject('your message')
->getSwiftMessage()
->getHeaders()
->addTextHeader('x-mailgun-native-send', 'true');
});

For those who need to add this configuration to all emails (like me), there is a practical way using listeners.
Create a Listener:
php artisan make:listener MessageSendingListener
Add the following content to the MessageSendingListener:
<?php
namespace App\Listeners;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Events\MessageSending;
use Illuminate\Queue\InteractsWithQueue;
class MessageSendingListener
{
/**
* Handle the event.
*
* #param \Illuminate\Mail\Events\MessageSending $event
* #return void
*/
public function handle(MessageSending $event)
{
$event->message
->getHeaders()
->addTextHeader('x-mailgun-native-send', 'true');
}
}
Add the configuration to the application's event mapping array in EventServiceProvider:
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
// ...
\Illuminate\Mail\Events\MessageSending::class => [
\App\Listeners\MessageSendingListener::class,
],
];
}
Obs.: Tested with Laravel 7.

Related

How to test the laravel Mailer using Phpunit

I need to test the Laravel Mailer using PHPunit, I am using CRUD Operations, where if any one the method fails, It should trigger the mail. I need to test the mail part, below is the code.
public function index()
{
$response = Http::withBasicAuth(userName,passWord)
->get(connection());
$this->html_mail($response);
return $response->json();
}
public function show($id)
{
$response = Http::withBasicAuth(userName, passWord)
->get(connection());
// check response & send mail if error
$this->html_mail($response);
$record = collect($response->json() ['output'])
->where($this->primaryKeyname, $id)->first();
return $record;
}
Mailer method:
public function html_mail($response)
{
if ($response->failed() || $response->serverError() || $response->clientError()) {
Mail::send([], [], function ($message) use ($response) {
$message->to('foo#example.com');
$message->subject('Sample test');
$message->setBody($response, 'text/html');
});
}
return 'Mail Sent Successfully';
}
}
Could someone please help to test the Mailer method using PHPunit.
Thanks.
It looks like there might be some code missing in your examples, but generally you're looking for Laravel's Mail::fake() method:
# tests/Feature/YourControllerTest.php
use Illuminate\Support\Facades\Mail;
/**
* #test
*/
public function index_should_send_an_email_if_authentication_fails(): void
{
Mail::fake();
$this->withToken('invalidToken', 'Basic')
->get('your.route.name');
Mail::assertSent(function ($mail) {
// Make any assertions you need to in here.
return $mail->hasTo('foo#example.com');
});
}
There's also an opportunity to clean up your controller methods here by leveraging middleware for authentication rather than repeating it in every method.
Digging into Illuminate\Auth\SessionGuard, Laravel automaticallys fire an Illuminate\Auth\Events\Failed event if authentication fails. Instead of sending directly from your controller, you might consider registering an event listener and attaching it to that event, then letting the listener dispatch a mailable notification.
# app/Providers/EventServiceProvider
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
'Illuminate\Auth\Events\Failed' => [
'App\\Listeners\\FailedAuthAttempt',
],
];
With those changes, your testing also becomes easier:
# tests/Feature/Notifications/FailedAuthAttemptTest.php
use App\Notifications\FailedAuthAttempt;
use Illuminate\Notifications\AnonymousNotifiable;
use Illuminate\Support\Facades\Notification;
/**
* #test
*/
public function it_should_send_an_email_upon_authentication_failure(): void
{
Notification::fake();
$this->withToken('invalidToken', 'Basic')
->get('your.route.name');
Notification::assertSentTo(new AnonymousNotifiable(), FailedAuthAttempt::class);
}
Now, any route in your application that uses Laravel's auth.basic middleware will automatically send the FailedAuthAttempt notification upon failure. This also makes it easier to, for example, send these notices to a Slack channel rather than sending emails.

Laravel Mail::assertQueued BadMethodCallException

I'm trying to write a Laravel PHPUnit test that checks if a mail has been queued after a user was created.
<?php
namespace Tests\Unit\User;
use App\User;
use Tests\TestCase;
use App\Notifications\UserCreated;
use Illuminate\Support\Facades\Mail;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Notification;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserUnitTest extends TestCase
{
use RefreshDatabase;
/**
* check if a user was created in database
*
* #return void
*/
public function testUserCreate()
{
$user = factory(User::class)->create();
$this->assertDatabaseHas('users', [
'email' => $user->email,
'active' => 0,
'activation_token' => $user->activation_token,
'deleted_at' => NULL
]);
}
/**
* check if email was sent after user was created in database
*
* #return void
*/
public function testEmailSentAfterUserCreated()
{
Notification::fake();
// Assert that no notifications were sent...
Notification::assertNothingSent();
$user = factory(User::class)->create();
// Assert a notification was sent to the given users...
Mail::assertQueued(UserCreated::class, 1);
}
}
When I run this test testEmailSentAfterUserCreated it throws the following exception.
There was 1 error:
1) Tests\Unit\User\UserUnitTest::testEmailSentAfterUserCreated
BadMethodCallException: Method Illuminate\Mail\Mailer::assertQueued
does not exist.
/home/vagrant/Projects/endiro/vendor/laravel/framework/src/Illuminate/Support/Traits/Macroable.php:103
/home/vagrant/Projects/endiro/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:245
/home/vagrant/Projects/endiro/tests/Unit/User/UserUnitTest.php:49
The Mail class has been included and I'm sure the arguments are correct but I'm not sure why I'm getting this error.
Use Mail::fake() if you want to assert on Mail::assertQueued(). I was facing the same issue. I forgot to add Mail::fake() in that particular test case.
Notifications does not have an assert queued, it has an assertSentTo().So an example of how it should look. If the notification can be queued, i would think you could use the Queue::fake() to achieve this.
Notification::assertSentTo(
[$user], UserCreated::class
);

How to listen messageSent event in laravel 5.5

Hi I am a beginner for events and listeners in laravel. So please explain me how to achieve this :
Aim :
Send an email to user. And know whether email is sent or not.
My Understanding :
Laravel has in-built event Illuminate\Mail\Events\MessageSent to fire after email is sent and I have to write a listener to listen the event.
What I did :
To send email :
Mail::to($receiverAddress)->send(new SendNewUserPassword($content));
This is working fine. Able to send email to user successfully.
To listen messageSent event, I created this listener :
<?php
namespace App\Listeners;
use Illuminate\Mail\Events\MessageSent;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class LogSentMessage
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param MessageSent $event
* #return void
*/
public function handle(MessageSent $event)
{
return $event->message;
}
}
To Register Event :
protected $listen = [
'App\Events\Event' => [
'App\Listeners\EventListener',
],
'Illuminate\Mail\Events\MessageSent' => [
'App\Listeners\LogSentMessage',
],
];
In Controller :
event(new MessageSent())
Please guide me how to return the message handled in Listener from controller. If my above approach is wrong explain me how to achieve it. This I am using for an api, so if sending mail is success/fail I want to know.
You can pass data from the controller to the mailable, and then from the mailable to the listener
For example I have a model called SendOrder that I use to keep track the status of the email, so I pass this model from the controller to the listener
This is what you have to do from scratch
In your controller
Pass the model to your Mailable constructor
$send_order = SendOrder::create(['status' => 'received', 'email' => 'foo#example.com']);
Mail::to($receiverAddress)->send(new SendNewMail($send_order));
In the Mailable SendNewMail
Class Mailable has a method withSwiftMessage() which you can use to store variables/objects that you can access later from the listener.
We will make a constructor that pass the model to the build() method where we can execute the withSwiftMessage() to store it for later.
use App\SendOrder;
class SendNewMail extends Mailable
{
protected $send_order;
public function __construct( SendOrder $send_order )
{
$this->send_order = $send_order;
}
public function build()
{
$send_order = $this->send_order;
$this->withSwiftMessage(function ($message) use($send_order) {
$message->send_order = $send_order;
});
// Do more stuffs
}
}
Create the listener
Register the event and listener in the file app/Providers/EventServiceProvider.php
protected $listen =
'Illuminate\Mail\Events\MessageSent' => [
'App\Listeners\LogSentMessage',
],
];
Now execute the command:
php artisan event:generate
This will automatically generate the new listener app\Listeners\LogSentMessage with a template code that's connected to the built-in event Illuminate\Mail\Events\MessageSent
In your Listener LogSentMessage
You can access the model this way:
public function handle(MessageSent $event)
{
$send_order = $event->message->send_order;
$send_order->update(['status' => 'sent']);
}
In your EventServiceProvider add your event and listener
protected $listen = [
'Illuminate\Notifications\Events\NotificationSent' => [
'App\Listeners\YourListenerClass',
],
];
and in YourListnerClass
public function handle(NotificationSent $event)
{
//access your $event data here
//which includes notification details too
}
Doing php php artisan event:generate will generate App\Listeners\LogSentMessage for you.
Edit the file for example:
public function handle(MessageSent $event)
{
dd($event->message->getBody(), $event->message->toString());
}
I was stuck forever and ever trying to get my EventServiceProvider to register the Illuminate\Mail\MessageSent event to a local listener. Every time that I tried to do
php artisan event:generate
I would get
Your application doesn't have any events matching the given criteria.
I finally found that my EventService provider was using
use Illuminate\Support\ServiceProvider;
when I switched this to use
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
The event was registered properly! Maybe this will help someone.

Laravel: saving a serialized copy of my model for history

I need to manage record history for a specific model(s).
So following the example here (https://laravel.com/docs/5.2/eloquent#events) I did something like this in my AppServiceProvider.php file:
use App\SourceModel;
use App\History;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider {
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
SourceModel::saving(function ($source) {
$his= new History();
$his->record = $source->toJson();
$his->user_id = Auth::User()->id;
$his->saved_id = $source->id;
$his->saved_type = 'App\SourceModel';
$his->save();
});
}
...
The problem is in this way Auth::User() returns NULL...
How can I solve this problem? is there a way to make Auth working in appserviceprovider or should i move my saving event somewhere else?
Since this closure is called when the model is saving, I would expect this to work assuming there is an authenticated user.
I was able to confirm this does work using tinker:
>>> App\User::saving(function ($user) { echo "AUTH USER ID: " . Auth::user()->id; });
=> null
>>> Auth::login(App\User::find(1));
=> null
>>> App\User::find(1)->save();
AUTH USER ID: 1⏎
=> true
Therefore, I would say that if Auth::user() returns null, this model was saved without an authenticated user and if that can happen you need to add a check:
SourceModel::saving(function ($source) {
$his= new History();
$his->record = $source->toJson();
$his->user_id = (Auth::check()) ? Auth::User()->id : 0;
$his->saved_id = $source->id;
$his->saved_type = 'App\SourceModel';
$his->save();
});
I think the right place to listen model events are EventServiceProvider ( App\Providers\EventServiceProvider ).
Simply move your code to "boot" method in EventServiceProvider and you are done.
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use App\SourceModel;
use App\History;
use Illuminate\Support\Facades\Auth;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
'App\Events\SomeEvent' => [
'App\Listeners\EventListener',
],
];
/**
* Register any events for your application.
*
* #return void
*/
public function boot()
{
parent::boot();
//
SourceModel::saving(function ($source) {
$his= new History();
$his->record = $source->toJson();
$his->user_id = Auth::User()->id;
$his->saved_id = $source->id;
$his->saved_type = 'App\SourceModel';
$his->save();
});
}
}
Note: you can include facades like this: "use \Auth"

Custom validator in Laravel 5

I am upgrading my Laravel application from 4 to 5. However, I have a custom validator that I cannot get to work.
In L4, I made a validators.php file and included it in global.php using require app_path().'/validators.php';.
I tried doing somewhat the same in L5. I dropped a validator in app/Validators/Validators.php, and updated my composer.json.
"files": [
"app/Validators/Validators.php"
]
However, now nothing renders on any page. What've I done wrong?
Try the following:
Make a bind class where you can implement each rule you want extending Validator class.
Make a service provider that extends ServiceProvider.
Add your custom validator provider at config/app.php file.
You can create the bind at Services folder like this:
namespace MyApp\Services;
class Validator extends \Illuminate\Validation\Validator{
public function validateFoo($attribute, $value, $parameters){
return $value == "foo"
}
}
Then, use a service provider to extends the core:
namespace MyApp\Providers;
use MyApp\Services\Validator;
use Illuminate\Support\ServiceProvider;
class ValidatorServiceProvider extends ServiceProvider{
public function boot()
{
\Validator::resolver(function($translator, $data, $rules, $messages)
{
return new Validator($translator, $data, $rules, $messages);
});
}
public function register()
{
}
}
Finally, import your service provider at config/app.php like so:
'providers' => [
...
...
'MyApp\Providers\ValidatorServiceProvider';
]
so here's what I did on adding a custom validation. this is for laravel 5.1
run PHP Artisan make:request MyFormValidationRequest file is created under app\Requests\MyFormValidationRequest.php
Here's the initial code:
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
class MyFormValidationRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
//
];
}
}
IMPORTANT: Change the return value of authorize() method to true, if you're not doing any authentication. it's initial value is false. else you get a white page with a "Forbidden" error message.
I added a rule under the function rules(), here's what it looks like
public function rules() {
return [
'activeuntil' => 'today_onwards'
];
}
today_onwards is my new validation.
I created a folder named 'Services' under App folder
I created a file named 'ValidatorExtended.php' under App\Services folder , here's the code below:
<?php
namespace App\Services;
use Illuminate\Validation\Validator;
use Carbon\Carbon;
class ValidatorExtended extends Validator {
private $_custom_messages = array(
"today_onwards" => "The :attribute must be today onwards",
);
public function __construct( $translator, $data, $rules, $messages = array(), $customAttributes = array() ) {
parent::__construct( $translator, $data, $rules, $messages, $customAttributes );
$this->_set_custom_stuff();
}
protected function _set_custom_stuff() {
//setup our custom error messages
$this->setCustomMessages( $this->_custom_messages );
}
protected function validateTodayOnwards( $attribute, $value ) {
$now = strtotime('-1 day');
$valueDateFormat = strtotime($value);
if($valueDateFormat > $now){
return true;
}
else {
return false;
}
}
}
Note: the validateTodayOnwards method is where you put your logic.
the name of the method should always start in "validate" then the name of your new validation key which should be in title case,
Another note your validation key should be separated by underscore and all small letters, in this case, "today_onwards". the underscore should be put before all first capital letters in the method name. I hope I explained it good.
TodayOnwards method is equivalent to validation name of "today_onwards",
another example, if I created validateOldPassword, your validation key should be "old_password".
I added below code in app\Providers\AppServiceProvider.php inside boot() method.
Validator::resolver(function($translator, $data, $rules, $messages = array(), $customAttributes = array())
{
return new ValidatorExtended($translator, $data, $rules, $messages, $customAttributes);
});
Don't forget to add below library, one is the Validator class and the other is your own class which is the "ValidatorExtended".
use App\Services\ValidatorExtended;
use Illuminate\Support\Facades\Validator;
Here's what the whole file looks like, [app\Providers\AppServiceProvider.php]
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\ValidatorExtended;
use Illuminate\Support\Facades\Validator;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
Validator::resolver(function($translator, $data, $rules, $messages = array(), $customAttributes = array())
{
return new ValidatorExtended($translator, $data, $rules, $messages, $customAttributes);
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
That's it. done. you created your own custom validation.
Additionally, if you want to use it in your controller, below is the code:
class testController extends Controller
{
public function updatePass(MiscValidation $request){
//code here
}
}
Instead of using Request Class you use your own class which is an extension of the Request class.

Categories