Send Mail laravel to multiple recipients by using foreach - laravel

Good evening everyone,
I have some issues in my project. I'm trying to send mail to each collaborator who celebrates his birthday. The data are taken from my database. The problem is that: if I have for example 2 or 3 collaborators who celebrate their birthday in the same day, only the first in the list receive a mail. My question how send to all concerned collaborator a mail.
My code:
public function handle()
{
$collaborateurs = Card::listCard();
foreach ($collaborateurs as $collaborateur) {
return Mail::to($collaborateur->adresse_email)->send(new SendEmail($collaborateur));
}
}
class CarteAnnif {
public static function listCard(){
$collaborateurs = Organigramme::whereMonth('date_de_naissance', now()->month)
->whereDay('date_de_naissance', now()->day)
->get();
$collaborateursConcernes = [];
foreach ($collaborateurs as $collaborateur) {
$date_de_naissance = Carbon::createFromFormat('d/m/Y', $collaborateur->date_de_naissance)->format('d-m');
$date_de_naissance = strtotime($date_de_naissance);
$today = date('d-m');
$today = strtotime($today);
if($date_de_naissance == $today ){
$collaborateursConcernes[] = $collaborateur;
}
}
return collect($collaborateursConcernes);
}
}
public $collaborateur;
public function __construct($collaborateur)
{
$this->collaborateur = $collaborateur;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
$this->view('admin.emails.send_card_to_collaborateurs')
->subject("Joyeux anniversaire");
return $this->from('app#domain.com')->view('admin.emails.send_card_to_collaborateurs');
}
}
I hope I have been clear otherwise I remain available for any other additional information
Thanks in advance!

The return ends a function, so your function will stop after the first loop. Just delete the retun :
foreach ($collaborateurs as $collaborateur) {
Mail::to($collaborateur->adresse_email)->send(new SendEmail($collaborateur));
}

Related

how to build a Laravel command that loops through users is sends unique emails to each user

I have a command that I'll run nightly using the Forge scheduler. The command simply loops through and sends emails to each user who qualifies for one.
COMMAND:
public function handle()
{
//Get all users
$users = User::all();
$data = [];
$renewalEmail = '';
foreach($users as $user)
{
//Check each users next_biling_date and see if is less than 72 hours from now, if so send reminder email
$nextBillingDate = ($user->hasSubscription())? $user->getSubscriptionData()->current_period_end : false;
$now = strtotime(now());
$threeDaysFromNow = 60*60*24*3;
//($user->hasSubscription())? $this->updateNextBillingDate($user) : false;//TODO: remove after working: follow up
if($user->hasSubscription() && $nextBillingDate-$now<=$threeDaysFromNow)
{
$data = [
'name' => $user->name,
'billingdate' => date('n/j/Y',strtotime($user->next_billing_date)),
];
// Log::info(print_r($data,true));
$renewalEmail = Mail::to('my#email.com')->send(new SubscriptionRenewalReminder($data));
// Log::info(print_r($renewalEmail,true));
}
}
return true;
}
My Mailable is pretty straight forward:
public function __construct($data)
{
$this->data = $data;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
Log::info('SubscriptionRenewalReminder Email build() called: ');
$firstName = explode(' ',$this->data['name'])[0];
$billingDate = $this->data['billingdate'];
Log::info('firstname: '.$firstName);
Log::info('billingDate: '.$billingDate);
return $this->view('emails.subscription-renewal-reminder')
->from('my#email.com', 'Project')
->subject('Project Subscription Is About To Renew')
->withName($firstName)
->withBillingdate($billingDate);
}
All of my Log::info's dump out the right information. I have 3 test users who all qualify to get the email.
In my testing, all three emails show the first user's name and billing date. Instead of unique emails, they are all identical.
I may move this into a queue but on a small set of users this should work fine. TIA

table relationship and how to use it in laravel controller

so, I have 2 tables, stage and event. Stage hasMany event, and Event belongsTo Stage. And I want to show all stage and its event as json. Here is my code in controller:
public function getschedule(){
$schedule = Stage::all();
//$event = Event_schedule2020::all();
if (!$schedule) {
return response()->json(['msg'=>'Error not found','code'=>'404']);
}
foreach($schedule->events as $array){
$datax[] = [
'id'=>$array->id,
'time'=>$array->time,
'category'=>$array->category,
'type'=>$array->title,
'designer'=>$array->designer,
];
}
foreach ($schedule as $item) {
$jadwal[] = [
'id'=>$item->id,
'date'=>$item->date,
'place'=>$item->stage,
'data'=>$datax,
];
}
return response()->json($jadwal);
}
but I always get this error
the error
so, is there anything I can do about this?
You can utilize inbuilt functions to do what you want to. Laravel automatically transforms model into JSON, no need to built arrays with it.
public function getschedule() {
// tell laravel you want to eager load events
$stages = Stage::with('events')->get();
// laravel knows you loaded events and therefor you can just return it and it does the rest automatically
return response()->json($stages);
}
in your Stage model you have to create relationship like this
public function events()
{
return $this->hasMany('App\Event');
}
then in your Controller
public function getschedule(){
$schedules = Stage::with('events')->get()->toArray();
return response()->json($schedules );
}
your mistake is call events on a collection for solve this you can change foreach like followings :
public function getschedule(){
$schedules = Stage::all(); // I add a 's' to $schedule because is better set plural name;
if (!$schedule) {
return response()->json(['msg'=>'Error not found','code'=>'404']);
}
foreach($schedules as $schedule){
$datax = [];
foreach($schedule->events as $event){
$datax[] = [
'id'=>$event->id,
'time'=>$event->time,
'category'=>$event->category,
'type'=>$event->title,
'designer'=>$event->designer,
];
}
$jadwal[] = [
'id'=>$item->id,
'date'=>$item->date,
'place'=>$item->stage,
'data'=>$datax,
];
}
return response()->json($jadwal);
}
but above solution is not recommended because send many request to server in any foreach loop, following solution is better:
public function getschedule(){
$schedules = Stage::with('events')->get(); // only this difference with above soloution and the rest is the same
if (!$schedule) {
return response()->json(['msg'=>'Error not found','code'=>'404']);
}
foreach($schedules as $schedule){
$datax = [];
foreach($schedule->events as $event){
$datax[] = [
'id'=>$event->id,
'time'=>$event->time,
'category'=>$event->category,
'type'=>$event->title,
'designer'=>$event->designer,
];
}
$jadwal[] = [
'id'=>$item->id,
'date'=>$item->date,
'place'=>$item->stage,
'data'=>$datax,
];
}
return response()->json($jadwal);
}

Eloquent: querying using a non-existing column

I'm trying the following: I have two models (Pub and Schedule) related by a 1xN relationship as follows:
Pub:
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function pubSchedules()
{
return $this->hasMany(Schedule::class);
}
Schedule:
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function pub()
{
return $this->belongsTo(Pub::class);
}
Table schedules has the following fields:
id | pub_id | week_day | opening_time | closing_time |
I use the following function to know if one pub is currently (or not) open:
/**
* #return bool
*/
public function isPubCurrentlyOpen()
{
$schedules = Schedule::where([
['pub_id', $this->id ],
['week_day', Carbon::now()->dayOfWeek],
])->get();
foreach ($schedules as $schedule){
$isOpen[] =
Carbon::now('Europe/Madrid')->between(
Carbon::now('Europe/Madrid')->setTimeFromTimeString($schedule->opening_time),
Carbon::now('Europe/Madrid')->setTimeFromTimeString($schedule->closing_time)
);
}
if(in_array(true, $isOpen)){
return true;
//return "Pub Opened";
}
return false;
//return "Pub Closed";
}
In my PubController I'd like, when the option "Filter by open pubs" is chosen if($request->openPubs == 1), to show only opened pubs isOpen ==true.
Knowing the relationships between models, how can I do it?
I'm looking for something like this:
if($request->openPubs == 1)
{
$pubs = $pubs->with('pubSchedules')->where('isOpen' == true);
}
Can you help me?
Thanks a lot!
You can do this using a "whereHas"
$openPubs = Pub::whereHas('schedule', function ($query) {
$query->where('week_day', Carbon::now()->dayOfWeek);
$query->whereRaw(
"'".Carbon::now('Europe/Madrid')->format("H:i:s")."' BETWEEN opening_time AND closing_time"
);
})->get();
This is assuming your opening time and closing time are the appropriate time format and not strings (though strings will work as well in a 24h format).
You might achieve something similar to what you are looking for by using a scope e.g.
public function scopeFilterBy($query, $filter = null) {
if ($filter == "isOpen") {
$query->whereHas('schedule', function ($query) {
$query->where('week_day', Carbon::now()->dayOfWeek);
$query->whereRaw(
"'".Carbon::now('Europe/Madrid')->format("H:i:s")."' BETWEEN opening_time AND closing_time"
);
});
}
return $query; //Not sure if this is needed
}
You could then do:
Pub::filterBy($request->openPubs ? "isOpen" : null)->get();
I don't fully understand how you are trying to accomplish this but it should be something like this
$pubs = Pub::with(['pubSchedules' => function ($query) {
$query->where('opening_time', '>' ,Carbon::now()) // make sure it's currently open
->where('closing_time', '<' ,Carbon::now()) // make sure that it's not finished already
->where('week_day', '==' ,Carbon::now()->dayOfWeek) // make sure it's today
}])->find($id);
// to get if pub is currently
if($pub->pubSchedules->count()){
//
}
you can put this code in the model (Pub) and make some changes
if you already have the object you can do this (Add it to model)
public function isPubOpen()
{
$this->load(['pubSchedules' =>
// same code in other method
]);
return (bool) $this->pubSchedules->count();
}
For small tables you could call the function isPubCurrentlyOpen for each element.
For this you would need to change your function to recieve the pub_id as a parameter:
public function isPubCurrentlyOpen($pub_id)
{
$schedules = Schedule::where([
['pub_id', $pub_id ],
['week_day', Carbon::now()->dayOfWeek],
])->get();
foreach ($schedules as $schedule){
$isOpen[] =
Carbon::now('Europe/Madrid')->between(
Carbon::now('Europe/Madrid')->setTimeFromTimeString($schedule->opening_time),
Carbon::now('Europe/Madrid')->setTimeFromTimeString($schedule->closing_time)
);
}
if(in_array(true, $isOpen)){
return true;
//return "Pub Opened";
}
return false;
//return "Pub Closed";
}
and to query the data do:
if($request->openPubs == 1)
{
// assuming $pubs is a collection instance
$pubs = $pubs->filter(function($a){
return $this->isPubCurrentlyOpen($a->id);
})
}
There's a feature in Eloquent called Eager Loading. The Eloquent ORM provides a simple syntax to query for all the Schedules that are related with this particular Pub as described below:
$pubIsOpen= $pub->schedules()
->where([
['week_day', Carbon::now()->dayOfWeek],
['opening_time' , '<' , Carbon::now('Europe/Madrid')],
['closing_time' , '>' , Carbon::now('Europe/Madrid')]
])
->count();
if($openPubCount > 0){
//PUB is open
}else{
//PUB is closed
}
If it helps to someone in the future I post my solution, thanks to #apokryfos:
Pub:
/**
* #param $pubs
* #return mixed
*/
public static function isPubCurrentlyOpen($pubs)
{
$pubs->whereHas( 'pubSchedules', function ($pubs) {
$pubs->where( 'week_day', Carbon::now()->dayOfWeek )
->whereRaw(
"'" . Carbon::now( 'Europe/Madrid' )->format( "H:i:s" ) . "' BETWEEN opening_time AND closing_time"
);
} );
return $pubs;
}
PubsController:
/**
* #param GetPubRequest $request
* #return ApiResponse
*/
public function getPubs(GetPubRequest $request)
{
$orderBy = 'id';
$order = 'asc';
$pubs = Pub::withDistance();
............
if($request->openPubs == 1)
{
$pubs = Pub::isPubCurrentlyOpen($pubs);
}
return $this->response(PubProfileResource::collection($pubs->orderBy($orderBy, $order)->paginate()));
}

laravel many to many with chaining where clause

I have a given table :
tools toolparts parts part_details
----- --------- ----- ------------
id* id* id* id*
name tool_id name part_id
part_id total (int)
----- --------- ----- ------------
the relation between Tools and Parts is ManyToMany. and the relation between parts and part_details is one to many.
with Laravel model, how can I get tool with part that has the biggest part_details.total ??
//tool model
public function parts()
{
return $this->belongsToMany('App\Part', 'tool_part');
}
//part model
public function tools()
{
return $this->belongsToMany('App\Tool', 'tool_part')
}
public function details(){
return $this->hasMany('App\Part_detail');
}
//partDetail model
public function part(){
return $this->belongsTo('App\Part');
}
Controller
public function index()
{
$tools = Tool::with('parts', 'parts.details')->has('parts')->get();
return $tools;
}
what I expected is something like :
Controller
public function index()
{
$tool = Tool::with('SinglePartThatHasHigestTotalInPartDetail');
}
Any Idea ??
You can use Laravel aggregates for querying to get the desired result,
In your code use max() function,
public function index()
{
$tool = Tool::with(['parts.part_details' => function ($query) {
$max = $query->max('total');
$query->where('total',$max);
})->first();
}
I haven't tested this but you can do like this.
Comment if you will get any errors.
I Manage my problem with "hacky" ways. if someone have a better and more elegant solution, please tell me.
//tool model
public function partWithHighestTotalDelivery($trans_date = null){
if (is_null($trans_date)) {
$trans_date = date('Y-m-d');
}
$parts = $this->parts;
$highest_total_delivery = 0;
foreach ($parts as $key => $part) {
$part->detail;
$total_delivery = $part->first_value;
if (isset( $part->detail->total_delivery )) {
$total_delivery += $part->detail->total_delivery;
}
if ($highest_total_delivery < $total_delivery ) {
$highest_total_delivery = $total_delivery;
$part->total_delivery = $highest_total_delivery;
$result = $part;
}
}
if (!isset($result)) {
$result = null;
}
$this->part = $result;
}
In controller i have :
public function index(Request $request){
$tools = Tool::has('parts')
->get();
$tools->each(function($tool){
$tool->partWithHighestTotalDelivery();
});
return $tools;
}
with this, I need to run tool->partWithHighestTotalDelivery() tools.count times. which is take noticeable process if the tools is many.
and also, the code I post and the question I ask has a slightly difference.that's for a simplicity sake's
Use the the hasManyThrough Relationship to get the all part details related to tool and then you can check the one by one record and get the highest total of the tool part.
// Tool Model
public function partsdetails()
{
return $this->hasManyThrough('App\PartDetail', 'App\Part','tool_id','part_id');
}
In Your controller
$data = Tool::all();
$array = [];
if(isset($data) && !empty($data)) {
foreach ($data as $key => $value) {
$array[$value->id] = Tool::find($value->id)->partsdetails()->max('total');
}
}
if(is_array($array) && !empty($array)) {
$maxs = array_keys($array, max($array));
print_r($maxs);// This array return the max total of the tool related parts
}
else{
echo "No Data Available";
}
You can start with the part detail and get the tool(s) from there:
$maxPartDetail = Part_detail::orderByDesc('total')->first();
$tools = $maxPartDetail->part->tools;

How to cache model attributes in Laravel

In my current configuration, a user's email is stored on a remote server that I need to hit with a curl quest.
Luckily, I only need the email once a day when a certain process runs. However, when that process does run it will need to reference the email multiple times.
This is the current accessor I have set up for email. The problem is the curl request is being called every time I use $user->email. What's the best way to avoid this?
in UserModel:
public function getEmailAttribute(){
$curl = new Curl;
$responseJson = $curl->post('https://www.dailycred.com/admin/api/user.json',array(
'client_id'=>getenv('dailycredId')
,'client_secret'=>getenv('dailycredSecret')
,'user_id'=>$this->id
));
$response = json_decode($responseJson);
return $response->email;
}
private $cached_email = false;
public function getEmailAttribute(){
if ($this->cached_email){
// if set return cached value
return $this->cached_email;
}
// get the email
$curl = new Curl;
$responseJson = $curl->post('https://www.dailycred.com/admin/api/user.json',array(
'client_id'=>getenv('dailycredId')
,'client_secret'=>getenv('dailycredSecret')
,'user_id'=>$this->id
));
$response = json_decode($responseJson);
// cache the value
$this->cached_email = $response->email;
// and return
return $this->cached_email;
}
Depending on your use case make adjustments (ie. session, cache , static property...).
Extend a the Eloquent Model class
namespace App\Models\Utils;
use Illuminate\Database\Eloquent\Model as OldModel;
class MyModel extends OldModel
{
private $cachedAttributes = [];
public function getCachedAttribute(string $key, Callable $callable)
{
if (!array_key_exists($key, $this->cachedAttributes)) {
$this->setCachedAttribute($key, call_user_func($callable));
}
return $this->cachedAttributes[$key];
}
public function setCachedAttribute(string $key, $value)
{
return $this->cachedAttributes[$key] = $value;
}
public function refresh()
{
unset($this->cachedAttributes);
return parent::refresh();
}
}
make your class
class ElementWithEmail extends MyModel
{
const ATTRIBUTE_KEY_FOR_EMAIL = 'Email';
public function getEmailAttribute(){
$key = self::ATTRIBUTE_KEY_FOR_EMAIL;
$callable = [$this, 'getEmail'];
return $this->getCachedAttribute($key, $callable);
}
protected function getEmail()
{
$curl = new Curl;
$responseJson = $curl->post('https://www.dailycred.com/admin/api/user.json',array(
'client_id'=>getenv('dailycredId')
,'client_secret'=>getenv('dailycredSecret')
,'user_id'=>$this->id
));
$response = json_decode($responseJson);
return $response->email;
}
}
Call it from your code
$element = new ElementWithEmail();
echo $element->email;

Resources