Hello I want to send e-mails rendered from templates that can be created / modifed by admin from a form (with placeholders).As in the example below and I want to send it to 100 users at the same time, I don't know how to do it, can you help?
Hi { { name } },
email: {{email } }
phone: { { phone } }
my controllers:
public function mailSend(Uye $uye, IcerikSablon $icerikSablon){
$dil_id=$uye->ulke->dil_id;
$icerikSablon->load(['translation'=>function($q)use ($dil_id){
$q->where('dil_id',$dil_id);
}]);
$icerik_sablon_icerik=$icerikSablon->translation->icerik;
$uye_name=$uye->name;
$uye_phone=$uye->phone;
$uye_mail=$uye->email;
$icerik_sablon_icerik= str_replace('{{name}}', $uye_name, $icerik_sablon_icerik;
$icerik_sablon_icerik=str_replace('{{phone}}', $uye_phone, $icerik_sablon_icerik);
$icerik_sablon_icerik=str_replace('{{email}}', $uye_mail, $icerik_sablon_icerik;
}
You should really look into the documentation for mailables and notifications for Laravel, it seems you are missing some basic setup. https://laravel.com/docs/8.x/mail
From the docs:
php artisan make:mail OrderShipped --markdown=emails.orders.shipped
Then pass in the parameters you want to dynamically have in the OrdersShipped class constructor, and assign them as variables in your class. You can then reference these variables within your blade template.
For example:
<?php
namespace App\Mail\Users;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class OrdersShipped extends Mailable
{
use Queueable, SerializesModels;
/**
* someVar
*
* #var $someVar;
*/
protected $someVar;
/**
* Create a new message instance.
*
* #param string $someVar
*
* #return void
*/
public function __construct($someVar)
{
$this->someVar = $someVar;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->from('test#test.com')
->subject('Password Reset Request')
// Pass your vars in here using `with`
->with([
'someVar' => $this->someVar,
])
->view('emails.orders.shipped');
}
}
In emails.order.shipped blade you will be able to reference {{ someVar }}
Related
I need to send confirmation emails on a checkout page. But with the current way I only pass $checkout.
This is the database structure of what data I need to pass:
this is how the $checkout gets passed to the Mailable
// send confirmation email to customer
Mail::to($request->email)->send(new CheckoutConfirmation($checkout));
Mailable:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use App\Models\Events;
class CheckoutConfirmation extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($checkout)
{
$this->checkout = $checkout;
// get the course data from the database
$event = Events::query()
->where('id', '=', $this->checkout->event_id)
->first();
// this needs to be passed, along $checkout
// $event->title;
// $event->start;
// $event->end;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->view('emails.checkout.customer')->subject('Wir freuen uns schon auf Sie! Hier ist die Bestätigung Ihrer Buchung.')->with('checkout', $this->checkout);
}
}
the issue with how it is currently handeled is that I can only call:
$checkout->xxx
but I also need to call $event->xxx in the email blade.
Look at this example
/**
* The user details.
*
* #var $details
*/
public $details;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($details)
{
$this->details = $details;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->to(data_get($this->details,'email'), env('APP_NAME'))
->replyTo(config('mail.from.address'), ucwords(data_get($this->details, 'name')))
->subject(data_get($this->details, 'subject'))
->view('emails.contact_us');
}
You can use $details;
then use data_get you can put $this->details then get the key whatever email or anything else .
In Controller when called Class Mail in this way
Mail::send(new ContactUsMail($request->validated()));
Based on this StackOverflow answer, I changed my Mailable to the following:
<?php
namespace App\Mail;
use App\Models\Events;
use App\Models\Checkout;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class CheckoutConfirmation extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($checkout)
{
$this->checkout = Checkout::query()->where('event_id', '=', $checkout->event_id)->first();;
$this->event = Events::query()->where('id', '=', $this->checkout->event_id)->first();
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->view('emails.checkout.customer')->subject('Wir freuen uns schon auf Sie! Hier ist die Bestätigung Ihrer Buchung.')->with(['checkout'=>$this->checkout, 'event'=>$this->event]);
}
}
now I can pass arrays to the template: ->with(['checkout'=>$this->checkout, 'event'=>$this->event])
this allows to add as much data as needed.
I'm trying to build a task scheduler for sending email routine and i got this error when i try to get approval_code from auth.
Here are my code in mail:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use App\Services\GetApprovalPersonByRouteCode;
use App\HabisKontrak;
class HabisKontrakMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
* #param HabisKontrak $habisKontrak
* #param [type] $person
* #param [type] $justInfo
*
*/
public function __construct()
{
//
// $this->link = $link;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
$reminder_kontrak=HabisKontrak::all();
$person = GetApprovalPersonByRouteCode::getPerson(auth()->user()->approval_code,HabisKontrak::KODE_MODUL);
return $this->subject('Pemberitahuan Karyawan Habis Kontrak')
->view('mail.reminder.habisKontrak')
->with([
'reminder_kontrak' => $reminder_kontrak,
'person' => $person
]);
// }
}
}
and here are code for the GetApprovalPersonByRouteCode
<?php
namespace App\Services;
use DB;
use App\PermintaanKendaraan;
class GetApprovalPersonByRouteCode{
/**
* Get person approval / just info
*
* #param string $approvalCode
* #param string $kode_modul
* #return void
*/
public static function getPerson($approvalCode, $kode_modul){
return DB::table('approvalroutedetail')
->select([
'approvalroutedetail.nik',
'approvalroutedetail.sequence',
'approvalroutedetail.just_info',
'approvalroutedetail.modul',
'approvalroutedetail.approvalroute_id',
'karyawan.nama',
'karyawan.departemensubsub',
'karyawan.email',
'karyawan.jabatan'
])->join('karyawan','approvalroutedetail.nik','karyawan.nik')
->where('approvalroutedetail.modul', $kode_modul)
->where('approvalroutedetail.approvalroute_id', $approvalCode)
->orderBy('approvalroutedetail.sequence','asc')
->orderBy('approvalroutedetail.just_info','asc')
->get();
}
}
I tried to pass the data via constructor like this
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use App\Services\GetApprovalPersonByRouteCode;
use App\HabisKontrak;
use App\Reminder;
use DateTime;
class HabisKontrakMail extends Mailable
{
use Queueable, SerializesModels;
protected $person; //approver (collection: GetApprovalPersonByRouteCode::getPerson)
/**
* Create a new message instance.
* #param HabisKontrak $habisKontrak
* #param [type] $person
* #param [type] $justInfo
*
*/
public function __construct($person)
{
//
$this->person = $person;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
$reminder_kontrak=HabisKontrak::all();
return $this->subject('Pemberitahuan Karyawan Habis Kontrak')
->view('mail.reminder.habisKontrak')
->with([
'reminder_kontrak' => $reminder_kontrak,
'person' => $this->person
]);
// }
}
}
And then i got the error massage
Too few arguments to function
App\Mail\HabisKontrakMail::__construct(), 0 passed
I try to get this value getPerson(auth()->user()->approval_code,HabisKontrak::KODE_MODUL), Thankyou.
Task scheduler does not have a user session, thus auth()->user() can only return null (non-object).
To fix this, your crontab command can provide the user argument along. And your command can get a user with arguments:
Arguments
All user supplied arguments and options are wrapped in curly braces. In the following example, the command defines one required argument: user:
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'mail:send {user}';
You may also make arguments optional or define default values for arguments:
// Optional argument...
mail:send {user?}
// Optional argument with default value...
mail:send {user=foo}
Simply pass the load a User object with the user argument (probably the user id or the user name). Pass this User on to HabisKontrakMail object, then you're good to go.
Wait. Doesn't that mean the same user is sending every time?
One catch is you can only have 1 user sending all the notification email. But according to the code you showed, the database don't seem to contain which user actually initiated the notification.
If you need to actually check it, you'll have to store the user id / username for the notification before it is scheduled. And I don't have enough information to suggest anything.
$created array contain email value and name and some more. return $this->view('emails.created'); works fine , but I want to make email sender be that $created->email.or my controller Mail::to('email')->from($created->email)->send(new Created($created)); like this. but two ways are not working. How can I approach, handle this variable array on my app\Mail\Created.php file? I need some helps.
class Created extends Mailable
{
use Queueable, SerializesModels;
/**
* The created instance.
*
* #var created
*/
public $created;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($created)
{
$this->created = $created;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->from($created->email)->view('emails.created');
}
}
Change your build function with this and check. Also make sure you pass email parameter in your array.
public function build()
{
return $this->from($this->created->email)->view('emails.created');
}
Trying to implement a temp workaround to deal with this weird SwiftMailer bug:
https://github.com/swiftmailer/swiftmailer/issues/762
When reading a file that has a length of exactly n*8192 bytes (n >= 1), the last > value of $bytes is an empty string which triggers the error.
#raffomania's comment on GitHub:
we found that the following adaption of AbstractFilterableInputStream.write would fix the problem for us:
public function write($bytes)
{
$this->_writeBuffer .= $bytes;
if (empty($this->_writeBuffer)) {
return;
}
foreach ($this->_filters as $filter) {
if ($filter->shouldBuffer($this->_writeBuffer)) {
return;
}
}
$this->_doWrite($this->_writeBuffer);
return ++$this->_sequence;
}
I'd like to extend the AbstractFilterableInputStream class and call this modified write method when AbstractFilterableInputStream is called by SwiftMailer.
I'm using the Laravel framework.
The best way to solve this is to have the swiftmailer forked and fixed and you use your own forked version of swiftmailer. However if you do not want to do this, this fix is rather lengthy but it should work. Give it a shot. If there is any issue do let me know.
1. Create app/Mail/CustomFileByteStream.php: This is to have your own version of write.
<?php
namespace App\Mail;
/**
* Allows reading and writing of bytes to and from a file.
*
* #author Chris Corbyn
*/
class CustomFileByteStream extends \Swift_ByteStream_FileByteStream
{
public function write($bytes)
{
$this->_writeBuffer .= $bytes;
if (empty($this->_writeBuffer)) {
return;
}
foreach ($this->_filters as $filter) {
if ($filter->shouldBuffer($this->_writeBuffer)) {
return;
}
}
$this->_doWrite($this->_writeBuffer);
return ++$this->_sequence;
}
}
2. Create app/Mail/CustomSwiftAttachment.php: So that it uses the custom FileByteStream
<?php
namespace App\Mail;
/**
* Attachment class for attaching files to a {#link Swift_Mime_Message}.
*
* #author Chris Corbyn
*/
class CustomSwiftAttachment extends \Swift_Attachment
{
/**
* Create a new Attachment from a filesystem path.
*
* #param string $path
* #param string $contentType optional
*
* #return Swift_Mime_Attachment
*/
public static function fromPath($path, $contentType = null)
{
return self::newInstance()->setFile(
new CustomFileByteStream($path),
$contentType
);
}
}
3. Create app/Mail/CustomSwiftImage.php: So that it uses the custom FileByteStream
<?php
namespace App\Mail;
/**
* An image, embedded in a multipart message.
*
* #author Chris Corbyn
*/
class CustomSwiftImage extends \Swift_Image
{
/**
* Create a new Image from a filesystem path.
*
* #param string $path
*
* #return Swift_Image
*/
public static function fromPath($path)
{
$image = self::newInstance()->setFile(
new CustomFileByteStream($path)
);
return $image;
}
}
4. Create app/Mail/Message.php: So that it uses your own custom Swift_Image and Swift_Attachment
<?php
namespace App\Mail;
use Illuminate\Mail\Message as DefaultMessage;
class Message extends DefaultMessage
{
/**
* Create a Swift Attachment instance.
*
* #param string $file
* #return CustomSwiftAttachment
*/
protected function createAttachmentFromPath($file)
{
return CustomSwiftAttachment::fromPath($file);
}
/**
* Embed a file in the message and get the CID.
*
* #param string $file
* #return string
*/
public function embed($file)
{
return $this->swift->embed(CustomSwiftImage::fromPath($file));
}
}
5. Create app/Mail/Mailer.php: So it uses your custom Message class
<?php
namespace App\Mail;
use Swift_Message;
use Illuminate\Mail\Mailer as DefaultMailer;
class Mailer extends DefaultMailer
{
/**
* Create a new message instance. Notice this is complete replacement of parent's version.
* We uses our own "Message" class instead of theirs.
*
* #return \Illuminate\Mail\Message
*/
protected function createMessage()
{
$message = new Message(new Swift_Message);
// If a global from address has been specified we will set it on every message
// instances so the developer does not have to repeat themselves every time
// they create a new message. We will just go ahead and push the address.
if (! empty($this->from['address'])) {
$message->from($this->from['address'], $this->from['name']);
}
return $message;
}
}
6. Create app/Mail/MailServiceProvider.php: So it uses your custom Mailer class
<?php
namespace App\Mail;
use Illuminate\Mail\MailServiceProvider as DefaultMailServiceProvider;
use App\Mail\Mailer;
/**
* This mail service provider is almost identical with the illuminate version, with the exception that
* we are hijacking with our own Message class
*/
class MailServiceProvider extends DefaultMailServiceProvider
{
/**
* Complete replacement of parent register class so we can
* overwrite the use of mailer class. Notice the "Mailer" class is points to our own
* version of mailer, so we can hijack the message class.
*
* #return void
*/
public function register()
{
$this->registerSwiftMailer();
$this->app->singleton('mailer', function ($app) {
// Once we have create the mailer instance, we will set a container instance
// on the mailer. This allows us to resolve mailer classes via containers
// for maximum testability on said classes instead of passing Closures.
$mailer = new Mailer(
$app['view'], $app['swift.mailer'], $app['events']
);
$this->setMailerDependencies($mailer, $app);
// If a "from" address is set, we will set it on the mailer so that all mail
// messages sent by the applications will utilize the same "from" address
// on each one, which makes the developer's life a lot more convenient.
$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;
});
}
}
7. Edit config/app.php
Comment out Illuminate\Mail\MailServiceProvider::class,
Add this below of the line above App\Mail\MailServiceProvider::class,
So this will use your custom MailServiceProvider. At any point of time if you wish to revert this, just delete all the files above and reverse this edit.
Calling sequence:
So here you go. This should hijack the MailServiceProvider to use your own custom Swift_ByteStream_FileByteStream. Hopefully no typos!
I've created a base class for my migrations. At the moment I run the artisan migrate command and it creates a new migration that extends the Migrations file, however I want to include my BaseMigration and extend it from there. I've been making this changes manualy but I feel like I'm repeating myself unnecessarily.
Any advice on how to have new migrations automatically extend and load my base migration?
It's doable in a fairly logical way, at least in Laravel 5
Subclass MigrationCreator and override getStubPath(), just copying the function over from the original class (it will use your subclass's __DIR__)
<?php
namespace App\Database;
use Illuminate\Database\Migrations\MigrationCreator;
class AppMigrationCreator extends MigrationCreator
{
public function getStubPath()
{
return __DIR__.'/stubs';
}
}
Write a service provider to override migration.creator with your own subclass (it must be a deferred service provider, because you cannot override a deferred binding with an eager one):
<?php
namespace App\Database;
use Illuminate\Support\ServiceProvider;
class AppMigrationServiceProvider extends ServiceProvider
{
protected $defer = true;
public function register()
{
$this->app->singleton('migration.creator', function ($app) {
return new AppMigrationCreator($app['files']);
});
}
public function provides()
{
return ['migration.creator'];
}
}
Add your service provider to config/app.php after the default ones.
Finally, copy vendor/laravel/framework/src/Illuminate/Database/Migrations/stubs alongside your MigrationCreator subclass (in this example it would become app/Database/stubs) and edit the templates to your needs.
Keep the DummyClass and DummyTable names, as they are replaced with str_replace() to create the actual migrations files.
Since Laravel 7 you can publish stubs using php artisan stub:publish.
The published stubs will be located within a stubs directory in the root of your application. Any changes you make to these stubs will be reflected when you generate their corresponding classes using Artisan make commands.
I don't think you can, because Laravel takes migrations from the vendor/laravel/framework/src/Illuminate/Database/Migrations/stubs folder and you cannot change that, but you have some options:
1) Create your own artisan command migrate:makemyown.
2) Use Jeffrey Way's Laravel Generators. They let you create your migrations by doing:
php artisan generate:migration create_posts_table --fields="title:string, description:text"
If you just have some fields you need to start with and not something more specific than that, it works really fine.
3) Edit Laravel stubs, but the problem is that as soon as you composer update they might get overwritten by Composer.
I believe that there is no way to override this (for now) but I think that you can create your custom command which will use Laravel logic. This was created for Laravel 5.
First you have to create Generator command app/Console/Commands/Generator.php:
<?php namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Console\Input\InputArgument;
class Generator extends Command
{
/**
* Command name
*
* #var string
*/
protected $name = 'generate';
/**
* Command description
*
* #var string
*/
protected $description = 'Custom object generator';
/**
* An array with all available generator classes
*
* #var array
*/
protected $types = ['request', 'model', 'middleware'];
/**
* Execute command
*
* #return mixed
*/
public function handle()
{
$type = $this->argument('type');
if (!in_array($type, $this->types)) {
return $this->error('Type must be one of: '.implode(', ', $this->types));
}
// Create new instance
$generatorClass = 'App\Console\Commands\Generators\\'.ucfirst($type);
$generator = new $generatorClass(new Filesystem());
// Each generator has "fire" method
$this->comment($generator->setClassName($this->argument('name'))->fire());
}
/**
* #return array
*/
public function getArguments()
{
return [
['type', InputArgument::REQUIRED, 'Type of class to generate: '.implode(', ', $this->types)],
['name', InputArgument::REQUIRED, 'Name of class to generate'],
];
}
}
Then you have to create an abstract class for all of your Generators classes app/Console/Commands/Generators/Generator.php:
<?php namespace App\Console\Commands\Generators;
use Illuminate\Console\GeneratorCommand;
abstract class Generator extends GeneratorCommand
{
// Directory name with whole application (by default app)
const APP_PATH = 'app';
/*
* Name and description of command wont be used
* Generators Commands are not loaded via Kernel
* Name and description property has been put just to avoid Exception thrown by Symfony Command class
*/
protected $name = 'fake';
protected $description = 'fake';
/**
* Class name to generate
*
* #var string
*/
protected $className;
/**
* Returns class name to generate
*
* #return string
*/
protected function getNameInput()
{
return $this->className;
}
/**
* Returns path under which class should be generated
*
* #param string $name
* #return string
*/
protected function getPath($name)
{
$name = str_replace($this->getAppNamespace(), '', $name);
return self::APP_PATH.'/'.str_replace('\\', '/', $name).'.php';
}
/**
* Sets class name to generate
*
* #param string $name
* #return $this
*/
public function setClassName($name)
{
$this->className = $name;
return $this;
}
/**
* Execute command
*
* #return string
*/
public function fire()
{
$name = $this->parseName($this->getNameInput());
if ($this->files->exists($path = $this->getPath($name)))
{
return $this->type.' already exists!';
}
$this->makeDirectory($path);
$this->files->put($path, $this->buildClass($name));
return $this->type.' '.$this->className.' created successfully.';
}
}
At the end you can create your first Generator class! app/Console/Commands/Generators/Request.php
<?php namespace App\Console\Commands\Generators;
class Request extends Generator
{
/**
* Class type to generate
*
* #var string
*/
protected $type = 'Request';
/**
* Returns default namespace for objects being generated
*
* #param string $rootNamespace
* #return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Http\Requests';
}
/**
* Returns path to custom stub
*
* #return string
*/
public function getStub()
{
return base_path('resources').'/stubs/request.stub';
}
}
Dont forget to add your generate command to Kernel app/Console/Kernel.php:
<?php namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel {
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
...
'App\Console\Commands\Generator',
...
];
Put your stubs under resources/stubs directory. Let's create first one for Request Generator resources/stubs/request.stub:
<?php namespace {{namespace}};
class {{class}} extends Request
{
/**
* #return bool
*/
public function authorize()
{
// CUSTOM LOGIC
return false;
}
/**
* #return array
*/
public function rules()
{
$rules = [];
// CUSTOM LOGIC
return $rules;
}
}
Then call with php artisan generate request MyRequest.
You can create your custom Model, Middleware, Controller etc. generators, it's very simple - you have to create new generator class under app/Commands/Console/Generators - take a look at Request.php generator to see how it works!
For Laravel 5 you'd edit one of the .stub files in:
vendor/laravel/framework/src/Illuminate/Database/Migrations/stubs
There's no reason why you can't edit those files.
Search in vendor/laravel/framework/src/ for .stub files to find all of the other stubs (templates) artisan uses.