I am building an email API on Laravel. I want to send emails via this API on my other projects. I send emails successfully through this API. However, the email content is hardcoded on the client side. For example:
$body = "Dear $user->name,<br />
Thank you for your order.";
$client->request('POST', 'http://api.test/send_mail?token=' . $token, ['form_params' => [
'from' => 'client#test.com',
'title' => 'API Test Mail',
'to' => 'customer#test.com',
'body' => $body,
'signature' => 'Regards'
]]);
My aim is especially creating dynamic email body without hardcoded. So, I save email content to the database. My plan is to trigger the saved email content with request on the client side. After some researches on email systems with similar structures, I think, I have to save the related content to the database like this format:
Dear {{ username }}
Thank you for your order.
By this way, I will only send the username info to the API from the client side. I will not have to deal with HTML email content.
However, I am not sure about what to do after this stage. How can I send and parse the $username info to the API from the client side? Do I have to add request to the API on every single parameters? Here is my current request rules:
return [
'title' => 'required',
'from' => 'required|email',
'to' => 'required|email',
'body' => 'required',
'signature' => 'nullable',
'email_template' => 'nullable'
];
Also this is my mail markdown:
#component('mail::message')
{!! $mail['body'] !!}
#if(array_key_exists('signature', $mail))
<p>{!! $mail['signature'] !!}</p>
#endif
#endcomponent
Actually, dynamic parameters will be child members of the body. If I want to add $username as a dynamic variable to the email body, should I add it to the rules of the request and also to the mail markdown? If so, I have to do some static transactions on every saved different email contents and their different parameters.
How can I build this structure in a clearer way?
Yes, You should parse every parameters to your API.
You may do like this:
$data['username'] = 'John';
$data['other'] = 'other';
$client->request('POST', 'http://api.test/send_mail?token=' . $token,
['form_params' =>
[
'from' => 'client#test.com',
'title' => 'API Test Mail',
'to' => 'customer#test.com',
'data' => $data,
'signature' => 'Regards',
'template_id' => 'template_id' // parse if you have multi templates
]]);
And you can send email like this in API controller.
public function sendEmail(Request $request)
{
$template = Template::findorfail($request->template_id);
$body = $template->body;// this is template dynamic body. You may get other parameters too from database. $title = $template->title; $from = $template->from;
foreach($request->data as $key=>$parameter)
{
$body = str_replace('{{'.$key.'}}', $parameter, $body); // this will replace {{username}} with $data['username']
}
$request->body = $body;
$mailObject = new MyMail($request); // you can make php artisan make:mail MyMail
Mail::to($request->to)->send($mailObject);
}
...
class MyMail extends Mailable
{
use Queueable, SerializesModels;
public $request;
public function __construct($request)
{
$this->request= $request;
}
public function build()
{
return $this->subject($this->request['title'])
->from($this->request['from'])
->markdown('components.mail.myTemplate');
}
}
Then in myTemplate.blade.php you can render {!!$request['body'] !}}
Hope this helps you.
Related
I have the following simple code to send an SMS message according to an incoming api request, but the message arrives at the phone with the accents replaced by their corresponding ascii versions.
What should I change in order for a message like "You won the áéíóúñ product" to arrive properly?
<?php
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
use Aws\Sns\SnsClient;
use Aws\Credentials\Credentials;
class Controller extends BaseController
{
public function send(Request $request){
$to = $request->input('phone');
$message = $request->input('message');
$client = new SnsClient([
'version' => '2010-03-31',
'credentials' => new Credentials(
env('SMS_AWS_ACCESS_KEY_ID'),
env('SMS_AWS_SECRET_ACCESS_KEY')
),
'region' => env('SMS_AWS_DEFAULT_REGION'),
]);
$client->SetSMSAttributes([
'attributes' => [
'DefaultSMSType' => 'Transactional',
]
]);
$client->publish([
'Message' => $message,
'PhoneNumber' => $to,
]);
return [$to, $message];
}
}
The package used to send the messages is the AWS SDK for PHP:
https://github.com/aws/aws-sdk-php
Official Documentation:
https://docs.aws.amazon.com/aws-sdk-php/v2/api/class-Aws.Sns.SnsClient.html
Some examples:
https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/sns-examples-sending-sms.html
This frustrated sad person has the only question I could find related to this issue:
https://forums.aws.amazon.com/thread.jspa?threadID=315153
I am using the AWS example to call the AWS SDK for PHP. When converting the code to a function I get the following error: Uncaught Error: Call to a member function sendEmail()
line 41 seems to be the issue: $result = $SesClient->sendEmail([
I have tried removing the result variable and commenting out its usage
When I run the code and its not a function its working fine, I am not sure what I am doing wrong here, and I am sure it could be a simple error.
Thanks for the help in advance
<?php
// If necessary, modify the path in the require statement below to refer to the
// location of your Composer autoload.php file.
require '/vendor/autoload.php';
use Aws\Ses\SesClient;
use Aws\Exception\AwsException;
// Create an SesClient. Change the value of the region parameter if you're
// using an AWS Region other than US West (Oregon). Change the value of the
// profile parameter if you want to use a profile in your credentials file
// other than the default.
$SesClient = new SesClient([
'profile' => 'default',
'version' => '2010-12-01',
'region' => 'us-east-1'
]);
function send_email($recipient, $message, $subject){
// Replace sender#example.com with your "From" address.
// This address must be verified with Amazon SES.
$sender_email = 'sender#gmail.com';
// Replace these sample addresses with the addresses of your recipients. If
// your account is still in the sandbox, these addresses must be verified.
// $recipient_emails = ['recipient1#example.com','recipient2#example.com'];
$recipient_emails = [$recipient];
// Specify a configuration set. If you do not want to use a configuration
// set, comment the following variable, and the
// 'ConfigurationSetName' => $configuration_set argument below.
// $configuration_set = 'ConfigSet';
$subject = $subject;
$plaintext_body = $message.PHP_EOL.'This email was sent with Amazon SES using the AWS SDK for PHP.' ;
$html_body = '<h1>'.$message.'</h1>';
$char_set = 'UTF-8';
try {
$result = $SesClient->sendEmail([
'Destination' => [
'ToAddresses' => $recipient_emails,
],
'ReplyToAddresses' => [$sender_email],
'Source' => $sender_email,
'Message' => [
'Body' => [
'Html' => [
'Charset' => $char_set,
'Data' => $html_body,
],
'Text' => [
'Charset' => $char_set,
'Data' => $plaintext_body,
],
],
'Subject' => [
'Charset' => $char_set,
'Data' => $subject,
],
],
// If you aren't using a configuration set, comment or delete the
// following line
// 'ConfigurationSetName' => $configuration_set,
]);
$messageId = $result['MessageId'];
echo("Email sent! Message ID: $messageId"."\n");
} catch (AwsException $e) {
// output error message if fails
echo $e->getMessage();
echo("The email was not sent. Error message: ".$e->getAwsErrorMessage()."\n");
echo "\n";
}
return 'success';
}
echo send_email('test#gmail.com', 'test', 'test subject');
Try declaring $SesClient inside of the send_email function
It's working without the function as you've declared $SesClient outside the function. Declare it after function - or pass it to the function via
function function send_email($recipient, $message, $subject, $SesClient) {
/* snip */
}
echo send_email('test#gmail.com', 'test', 'test subject', $SesClient);
Currently Using:
Laravel 5.5
"tucker-eric/docusign-rest-client": "^1.0",
"tucker-eric/laravel-docusign": "^0.1.1"
Intention is to generate a URL so all customers / agents sign on the spot
Here is what I have so far
I first create the client
$client = new DocuSign\Rest\Client([
'username' => env('DOCUSIGN_USERNAME'),
'password' => env('DOCUSIGN_PASSWORD'),
'integrator_key' => env('DOCUSIGN_INTEGRATOR_KEY'),
'host' => env('DOCUSIGN_HOST')
]);
For each signer I assign their name and email
$templateRole1 = $client->templateRole([
'email' => 'abc#gmail.com',
'name' => 'abc',
'role_name' => 'Agent'
]);
$templateRole2 = $client->templateRole([
'email' => 'abc123#gmail.com',
'name' => 'abc',
'role_name' => 'Purchaser 1'
]);
$templateRole3 = $client->templateRole([
'email' => 'abc124#gmail.com',
'name' => 'abc124',
'role_name' => 'Purchaser 2'
]);
$templateRole4 = $client->templateRole([
'email' => 'abc125#gmail.com',
'name' => 'abc125',
'role_name' => 'Seller'
]);
I create the envelope (not sure why it sends it, I dont want it sent yet
$envelopeDefinition = $client->envelopeDefinition([
'status' => 'sent',
'email_subject' => '[DocuSign PHP SDK] - Signature Request Sample',
'template_id' => '***abc-123-',
'template_roles' => [
$templateRole1,
$templateRole2,
$templateRole3,
$templateRole4,
],
]);
Envelope options just because even tho I don't have any
$envelopeOptions = $client->envelopes->createEnvelopeOptions([]);
Creates the final envelope
$envelopeSummary = $client->envelopes->createEnvelope($envelopeDefinition, $envelopeOptions);
Prepare the embedding so I can extract the URL
$envelopeApi = $client->envelopes;
$recipient_view_request = new \DocuSign\eSign\Model\RecipientViewRequest();
$recipient_view_request->setReturnUrl('https://www.example.net/callback/docusign');
$recipient_view_request->setClientUserId((string) $client->getAccountId());
$recipient_view_request->setAuthenticationMethod("None");
try {
$signingView = $envelopeApi->createRecipientView($client->getAccountId(), $envelopeSummary->getEnvelopeId(), $recipient_view_request);
} catch (DocuSign\eSign\ApiException $e){
echo "Error connecting Docusign : " . $e->getResponseBody()->errorCode . " " . $e->getResponseBody()->message;
}
Which returns:
object(DocuSign\eSign\Model\ErrorDetails)#419 (1) { ["container":protected]=> array(2) { ["error_code"]=> string(20) "INVALID_REQUEST_BODY" ["message"]=> string(94) "The request body is missing or improperly formatted. Input string was not in a correct format." } } Error connecting Docusign : INVALID_REQUEST_BODY The request body is missing or improperly formatted. Input string was not in a correct format.done
My question is what I'm doing wrong to get this error returned, and why is it sending the email to the people signing as I didn't explicitly tell it
Thanks
I'm not familiar with the DocuSign Laravel facades by Eric Tucker. If you need to add attributes beyond what Eric's facades provide then you'll need to fork that project to add support for the additional attributes.
You have a server-resident template. You want to use it to provide an embedded signing ceremony in your Laravel app for the signers.
For a signer recipient to be marked as an embedded signer, set the client_user_id attribute to the signer object. For example:
$templateRole1 = $client->templateRole([
'email' => 'abc#gmail.com',
'name' => 'abc',
'role_name' => 'Agent',
'client_user_id' => '1000'
]);
Note that the client_user_id should uniquely identify this signer as a user within your application.
Re: Why are the signers receiving email invites to sign?
Setting the client_user_id will suppress the email notification to the signer.
Re: should the envelope be sent or be in draft status?
You want sent status, which enables recipients to sign via the embedded signing ceremony you'll be next creating.
Re: Envelope Options for creating the envelope.
Normally, you don't supply an EnvelopeOptions when creating an envelope with the PHP SDK. However, Eric Tucker could be combining calls or something. You'll need to check his code.
Here is a standard PHP call to send an envelope:
$config = new \DocuSign\eSign\Configuration();
$config->setHost($args['base_path']);
$config->addDefaultHeader('Authorization', 'Bearer ' . $args['ds_access_token']);
$api_client = new \DocuSign\eSign\ApiClient($config);
$envelope_api = new \DocuSign\eSign\Api\EnvelopesApi($api_client);
$results = $envelope_api->createEnvelope($args['account_id'], $envelope_definition);
$envelope_id = $results->getEnvelopeId();
Obtaining the redirect URL for the embedded signing ceremony
Normal PHP way to do this is to call the createRecipientView method. You need to provide the signer's name, email, and client_user_id from the create envelope step, along with the authentication method your app is using to identify the signer. And, of course, the envelope id too.
Example:
# Create the Recipient View request object
$authentication_method = 'None'; # How is this application authenticating
# the signer? See the `authenticationMethod' definition
# https://developers.docusign.com/esign-rest-api/reference/Envelopes/EnvelopeViews/createRecipient
$recipient_view_request = new \DocuSign\eSign\Model\RecipientViewRequest([
'authentication_method' => $authentication_method,
'client_user_id' => $envelope_args['signer_client_id'],
'recipient_id' => '1',
'return_url' => $envelope_args['ds_return_url'],
'user_name' => $envelope_args['signer_name'],
'email' => $envelope_args['signer_email']
]);
# 4. Obtain the recipient_view_url for the signing ceremony
# Exceptions will be caught by the calling function
$results = $envelope_api->createRecipientView($args['account_id'], $envelope_id,
$recipient_view_request);
$redirect_url = $results['url'];
Is there an easy way to "override" the destination of an email using CakeEmail?
I have a model method that send an email using this piece of code:
$Email = new CakeEmail();
$Email->config('default');
$sent = $Email->template('new-notification')
->subject('A new notification for you')
->viewVars([
'name' => $foo['User']['name'],
'text' => $foo['Notification']['text'],
)
->emailFormat('html')
->to($foo['User']['email'])
->send();
And this configuration:
class EmailConfig
{
public $default = array(
'host' => 'smtp.server.com',
'port' => 587,
'username' => 'user#domain.com',
'password' => 'pwdAsYouKnow',
'from' => array('noreply#domain.com' => 'Company'),
'transport' => 'Smtp'
);
}
As you can see, I send the email for a dynamically defined user's email.
My objective is when I'm developing locally on my machine, to force every call to ->send() to force a destination like developer#domain.com.
I can easily detect if I'm on development, but I don't know how to force in a "master" way to CakeEmail only send to a defined account overriding the one set on the call.
I already tried to set an
'to' => array('developer#domain.com' => 'Dev'),
inside $default but, no success.
I appreciate any help, thanks!
i'm assuming when you are on local machine you run in debug mode, so you can check if debug mode is on then use that to send to different email
if(Configure::read('debug')){
$emailTo = 'developer#domain.com'
}
else{
$emailTo = $foo['User']['email'];
}
then you just use variable for email address:
$sent = $Email->template('new-notification')
->subject('A new notification for you')
->viewVars([
'name' => $foo['User']['name'],
'text' => $foo['Notification']['text'],
)
->emailFormat('html')
->to($emailTo)
->send();
Im trying to use the Mail::queue to send and email, but when I call this function it simple sends the mail, and the response is delayed ... I thought that the point of using Mail::queue was to queue ....
I want the response to came instantly, not having to wait for the email to be sent
for eg
Mail::queue('emails.template', $data, function($message) {
$message->to('somemail#gmail.com');
$message->subject('Notificacion');
});
return Response::json(array('error' => 0, 'message' => 'Ok'));
I want to receive the response without waiting for the mail to be sent.
How can I do that???
What queue driver (app/config/queue.php - 'default' param) are you using? If you're using sync, and haven't set up one of the others, then you're using the synchronous driver, which does exactly what the name says: Runs your queued task as soon as the task is created.
You need to configure an MQ server for Laravel to talk to. You can get a free iron.io account for this, and then you need to configure it, for instance:
'iron' => array(
'driver' => 'iron',
'project' => 'iron-io-project-id',
'token' => 'iron-io-queue-token',
'queue' => 'queue-name',
),
Then when you use Mail::queue() it will push the instruction to iron.io. You'll then have to have another thread listening on the queue - just run php artisan queue:listen and leave it running while messages are pushed to the queue.
/**
* Get all email recipients and include their user details for Mailgun's
* template tags - %recipient.userToken%
*/
private function getRecipients()
{
foreach (User::get() as $user)
{
$this->recipients[$user->email] = [
'id' => $user->id,
'userToken' => $user->user_token,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'email' => $user->email
];
}
}
private function sendEmail()
{
$subject = 'Demo Subject';
/**
* Data for the Blade template
*/
$data = [
'foo' => 'bar'
];
// Inline the CSS for the email
$inliner = new InlineEmail('emails.some-email', $data);
$content = $inliner->convert();
// Create Emails table entry for this email. Used for Mailgun webhooks
$email = Email::create(['user_id' => $this->userId, 'subject' => $subject, 'email_id' => str_random()]);
// Prepare the email addresses
$emailAddresses = array_column($this->recipients, 'email');
$this->mailgun->sendMessage('demo.org', [
"from" => 'support#demo.org',
"to" => implode(',', $emailAddresses), // Comma separated list of email addresses
"subject" => $subject,
"html" => $content, // Inlined CSS HTML from Blade
"text" => "Plain text message here",
"recipient-variables" => json_encode($this->recipients), // Required for batch sending, matches to recipient details
"v:messageId" => $email->id, // Custom variable used for webhooks
]);
}