Email sending works fine when I have a global 'to' set in config/mail.php
'to' => [
'address' => 'someone#example.com',
'name' => 'Someone',
however as soon as this is removed and email is supposedly sent to the actual email address, nothing happens. No errors, everything appears fine, except the email is never received.
I've checked spam folders, I've tried sending to different email addresses, I've tried setting the notifiable route (even though 'email' exists on the model), I've cleared cache and config cache, I've tried listening to message sending event and dumping results - I'm lost.
solved.
When sending a MailableObject (as apposed to a MailMessage) you need to chain the 'to' command like so:
public function toMail($notifiable)
{
return (new MailObject(blah blah))->to($notifiable);
}
Related
I add job on queue on page load controller and invoke laravel event and broadcast it via socket io to front end. Problem is since this is done on page load, the job is executed before the page fully loaded. As a result, I can see the response appended for a short time while the page loads and disappeared when its fully loaded. Why is that so?
I doubted if the connection is on sync instead of redis. Upon checking .env and config/queue.php uses redis as default.
DispatchNow is working fine but dispatch is not. Is it due to this the response sent immediately before the page settled down?
In front end, I added the code to connect with socket inside document ready to ensure it's done after the dom is loaded. But doesnt help, it behaves the same.
I tried other workaround where I fire an ajax call to the queue job once the specific DOM element is visible, and it works fine.
But I want it to be called on the page controller itself instead of a separate ajax.
In controller:
$sellings = curl(...some call to external url);
SendOrder::dispatchNow($sellings, Auth::id());
return view('home');
In SendOrder job:
public function handle()
{
// Allow only 2 emails every 1 second
Redis::throttle('any_key')->allow(2)->every(1)->then(function () {
event(new DashboardEvent('job1', $this->order, $this->user));
Log::info('job 1done');
}, function () {
// Could not obtain lock; this job will be re-queued
return $this->release(2);
});
}
.env:
BROADCAST_DRIVER=redis
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
config/queue.php
'default' => env('QUEUE_CONNECTION', 'redis'),
........
.........
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90,
'block_for' => null,
],
You could simply delay the dispatching with something like:
SendOrder::dispatch()->delay(now()->addMinutes(1))
Also, if you what to dispatch a job after the response is sent back to the client, you could use dispatchAfterResponse() method too.
This method makes the job run after the response is sent and before closing the connection. It simply registers a terminating callback that the application runs before it’s done with the request.
However, this is only useful to dispatch a short job instantly instead of sending it to a queue system. Since sending emails isn't exactly a short job, this might not be for you.
I'm working with a legacy Cakephp 2 app and need to create users via AJAX post on another domain.
I've got the whole thing working nicely in my local environment but have been battling with my prod environment.
I am using Postman to form a consistent Post request and setting the various headers as well as setting data values.
Locally:
I send a post request to a URL and var_dump the entire request object into the response. I can see that my data is populated. $this->request->data['email'] returns exactly what I expect.
Production:
I deploy the exact same code and my data array is completely empty.
I have set my Access-Control-Allow headers and I'm not getting any authisation issues. I can interact with the request within the application but I can not access any data. The request is the same request just a different endpoint.
I am running identical versions of PHP and exactly the same codebase.
Can anyone think of any environmental factors that might affect the request data?
This is my controller code in brief:
public function remoteadd() {
var_dump($this->request);
if ($this->request->is('ajax')) {
$this->disableCache();
$this->autoRender = false;
$this->response->type('json');
$this->User->create();
$gen_pass = $this->generatePassword();
$this->request->data['password'] = $gen_pass;
$emailAddr = $this->request->data['email'];
// Check if this email exists
$conditions = array(
'User.email' => $emailAddr,
);
if (!$this->User->hasAny($conditions)) {
if ($this->User->save($this->request->data)) {
$this->response->statusCode(200);
$this->response->body(json_encode(
array('status' => 'success', 'message' => 'New account successfully created')
));
}
} else {
$this->response->statusCode(500);
$this->response->body(json_encode(
array('status' => 'error', 'message' => 'Email address already exists')
));
}
$this->response->send();
$this->_stop();
}
}
It seems like the issue related to CORS preflight. Two requests are actually triggered. The first is a preflight which given my controller action is not returning any data as it's not actually a legitimate post request. The second request/response has the data appropriately loaded as expected.
In my application each user can use his own SMTP server. Therefor the config must be provided. I'm using Laravel Notifications to send the emails. If I'm using no queue (that means sync), there is no problem.
I made a CustomNotifiable Trait:
config([
'mail.host' => $setting->smtp_host,
'mail.port' => $setting->smtp_port,
'mail.username' => $setting->smtp_username,
'mail.password' => $setting->smtp_password,
'mail.encryption' => $setting->smtp_encryption,
'mail.from.address' => $setting->smtp_from_address,
'mail.from.name' => $setting->smtp_from_name,
]);
(new \Illuminate\Mail\MailServiceProvider(app()))->register();
After that, I restore the original config:
config([
'mail' => $originalMailConfig
]);
(new \Illuminate\Mail\MailServiceProvider(app()))->register();
No problem until now.
But if it's queued, just the first config after starting the queue worker will be taken for all further emails, even if any other SMTP config is provided. The default config from config/mail.php will be overridden. But this only works the first time.
I've made in the AppServiceProvider::boot method (the SMTP config is stored at the notification):
Queue::before(function (JobProcessing $event) {
// Handle queued notifications before they get executed
if (isset($event->job->payload()['data']['command']))
{
$payload = $event->job->payload();
$command = unserialize($payload['data']['command']);
// setting dynamic SMTP data if required
if (isset($command->notification->setting))
{
config([
'mail.host' => $command->notification->setting->smtp_host,
'mail.port' => $command->notification->setting->smtp_port,
'mail.username' => $command->notification->setting->smtp_username,
'mail.password' => $command->notification->setting->smtp_password,
'mail.encryption' => $command->notification->setting->smtp_encryption,
'mail.from.address' => $command->notification->setting->smtp_from_address,
'mail.from.name' => $command->notification->setting->smtp_from_name,
]);
(new \Illuminate\Mail\MailServiceProvider(app()))->register();
}
}
});
Of course, the original config get restored:
Queue::after(function (JobProcessed $event) use ($originalMailConfig) {
$payload = $event->job->payload();
$command = unserialize($payload['data']['command']);
// restore global mail settings
if (isset($command->notification->setting))
{
config([
'mail' => $originalMailConfig
]);
(new \Illuminate\Mail\MailServiceProvider(app()))->register();
}
});
It seems, as the Swift Mailer has a cache or something like that. I registered a new MailServiceProvider, which should simply replace the old one. So if I set the config with the new SMTP data, the new registered provider should take them. Logging the config shows even in the TransportManager, that the correct SMTP data were set, right before sending the mail, but the mail was sent with the first set config.
I found this thread and tried the linked solution, but with the same result: How to set dynamic SMTP details laravel
So I need a way to override the Services / ServiceProvider / SMTP config. Even if the Supervisor restarts the queue, there is a chance that multiple emails with different configs should be send at the same time.
In Laravel 5.4+, as I see that the Mailer Class is a singleton that hold a MailTransport Class, which is responsible for the config of SMTP mail and is a singleton,too; I just have to override the config using the following approach:
First, I setup a trait so I can just turn this feature on some Mails:
trait MailSenderChangeable
{
/**
* #param array $settings
*/
public function changeMailSender($settings)
{
$mailTransport = app()->make('mailer')->getSwiftMailer()->getTransport();
if ($mailTransport instanceof \Swift_SmtpTransport) {
/** #var \Swift_SmtpTransport $mailTransport */
$mailTransport->setUsername($settings['email']);
$mailTransport->setPassword($settings['password']);
}
}
}
Then, in the build() method of your mail class, you can utilize the above trait and call:
$this->changeMailSender([
'email'=>$this->company->email,
'password'=>$this->company->email_password,
]);
Boom, let the Laravel do the rest.
After a lot of researching I stumbled upon the different queue commands. I tried queue:listen (which is not described in the Laravel 5.4 docs) instead of queue:work and the problems are gone.
Of course, this doesn't really explain the described behavior, but fortunately it doesn't matter, because I can live with this solution/workaround.
Another strange behavior is, that from time to time the queue worker throws an exception because the database was locked. No idea, when or why this happened.
This post explained a little bit, why things can happen: What is the difference between queue:work --daemon and queue:listen
In a nutshell, queue:listen solved my problem and another very strange db lock problem as well.
I have problem with the Laravel's Mail. I have nothing wrong with the source code to send an email. The email was sent but the email address of sender is mine instead of sender. It is the one in the config file. The replyTo was ignored also.
My code is as following:
Mail::send('appointments.emails.new_appointment', ['data' => $data], function($message) use ($sender)
{
$message->from($sender['email'], $sender['name'])
->to('myemail#gmail.com', 'My Name')
->replyTo($sender['email'], $sender['name'])
->subject('Contact from ' . $sender['name']);
});
I received an email with sender name and my email address. And, when I click on rely, it replys to myself. That means the replyTo didn't work. How can I fix that?
Sorry if my English is not so good.
In Laravel you can make a custom messages for validators. But I found that the prepared messages are little bit wrong. With before and after validation rules, the parameter is converted with strtotime like that said in the documentation.
So if I set rule 'expires' => 'before:+1 year' the rule is working and correct. But if the user inputs a wrong value, Laravel prints the message like that:
The expires must be a date before +1 year
This message is very unclear for the average client. I was expected that the message will be converted with the strtotime also.
There is a clean way to print more clear error message?
You can override the validation messages with a custom ones if you want to.
In the Form Request class, add the following method and change the messages like so:
public function messages()
{
return [
// 'fieldname.rulename' => 'Custom message goes here.'
'email.required' => 'Er, you forgot your email address!',
'email.unique' => 'Email already taken m8',
];
}
Update:
If you want to change the global messages, you can open the validation.php file inside resources/lang/en/validation.php and edit the message there.
You can user AppServiceProvider.php
and you can write your new validation rule there and set your error message in validation.php file
/app/Providers/AppServiceProvider.php
/resources/lang/en/validation.php
Ex:
Validator::extend('before_equal', function ($attribute, $value, $parameters) {
return strtotime(Input::get($parameters[0])) >= strtotime($value);
});
and the message is
'before_equal' => 'The :attribute field is only accept date format before or equal to end date',
Please try this and you can alter this to based on your require.