I am writing up a cron job for daily email notification.
Here are the scenario lets say
User A gets 10 leads a day.
User B gets 5 leads a day.
User C gets 2 leads a day.
i want to send one email to User A having 10 leads information in it.
then one email to User B having 5 leads information in it.
then one email to User C having 2 leads information in it.
so I want to try to create a summary email having lead information in it for a particular user.
foreach ($today_leads as $today_lead) {
$data[] = [
'user_id' => $today_lead->user_id,
'user_fullname' => $today_lead->user_fullname,
'user_email' => $today_lead->user_email,
'lead_firstname' => $today_lead->first_name,
'lead_lastname' => $today_lead->last_name,
'lead_email' => $today_lead->email,
'lead_phone' => $today_lead->phone,
'lead_source' => $today_lead->source,
];
Mail::to(data['user_email'])->send(new DailyLeadSummary($data));
}
if I write my in foreach loop then I end up sending 10 emails to User A, 5 emails to User B, and so on.
Any other approach to handle this?
You can first group your results based on user_email and then send email in second loop.
$grouped_data = [];
foreach ($today_leads as $lead) {
$grouped_data[$lead->user_email][] = [
'user_id' => $lead->user_id,
'user_fullname' => $lead->user_fullname,
'user_email' => $lead->user_email, // you can remove it if not required in email body anywhere
'lead_firstname' => $lead->first_name,
'lead_lastname' => $lead->last_name,
'lead_email' => $lead->email,
'lead_phone' => $lead->phone,
'lead_source' => $lead->source,
];
}
foreach($grouped_data AS $user_email => $data)
{
Mail::to($user_email)->send(new DailyLeadSummary($data));
}
Looking at your implementation, you're on the right path, but you need to make some adjustments.
first of all, you need to query the user table to get users with leads on the particular day. do that like this
// This will return only users who got leads today
$usersWithLeads = User::whereHas('leads', function($query) {
// This will match all leads that was created today
$query->where('created_at', >=, now()->toStartOfDay());
})->get();
// then you can loop over $usersWithLeads
foreach($usersWithLeads as $usersWithLead) {
// You can then retrieve the leads gotten today via the leads relationship
$leads = $usersWithLead->leads()->where('created_at', now()->startOfDay());
// Then send your mail
Mail::to($usersWithLead)->send(new DailyLeadSummary($leads));
}
You can then render the leads on your mailable view however you like. But this makes sure you send only one email per user.
Also, You can modify the query to retrieve $usersWithLeads to also retrieve the daily leads like this:
$usersWithLeads = User::with(['leads' => function ($query) {
$query->where('created_at', now()->toStartOfDay());
}])->whereHas('leads', function($query) {
// This will match all leads that was created today
$query->where('created_at', >=, now()->toStartOfDay());
})->get();
Then your loop will look like:
foreach($usersWithLeads as $usersWithLead) {
// Then send your mail
Mail::to($usersWithLead)->send(new DailyLeadSummary($usersWithLead->leads));
}
Note: the now() method used in the query is a Carbon helper method for Carbon::now() which gives you the current time, chaining the ->startOfDay() method on now() retrieves the datetime at 0:00 (when the day started)
This Answer also assumes you have a table to hold leads and have declared a relationship in your User model like:
public function leads() {
return $this->hasMany(App\Leads::class, 'user_id', 'id');
}
Let me know if you need more help with this.
Related
I'm making a Queueing system and I want the called queue number to go back to last queue in case the user doesn't show for the transaction for a given period of time. Please help me how to do this.
This is my function for calling a queue:
public function callqueue(Request $request)
{
$request->validate([
'called' => ['max:255'],
'counter' => ['max:255']
]);
$dept = Auth::user()->department;
$call = Queue::where([
['department',$dept],
['called', 'no']
])
->whereDate('created_at', Carbon::today())
->orderBy('id', 'asc')
->first();
if ( ! is_null($call)) {
$call->update([
'called' => $request->called,
'counter' => $request->counter
]);
$call->save();
event(new NewQueue($call));
return redirect('admin')->withStatus(__('Queue has been called.'));
} else {
return redirect('admin')->withStatus(__('No available queue for calling.'));
}
}
I have created similar queue system, do comment if you need any modification
I want the called queue number to go back to last queue in case the
user doesn't show for the transaction for a given period of time.
On queue table put 2 timestamps last_updated_at and next_update_at
Pick the first in queue ordering by nexy_update_at
$call=Queue::...
->where('next_update_at', '>', Carbon::now()->timestamp) # I recommend to use timestamp not datetime
->orderBy('next_update_at', 'asc')
->first();
if ($call) {
// do the task
}
Now whenever person shows up in website or anything you wish, just pick his queue task and update the next_update_at
Queue::where('user_id', ...)
->...
->update(['next_update_at', now()->timestamp]);
EDIT:
I want to thanks #jimmix for giving me some idea to get started on my last post, But unfortunately, my post was put on hold. Due to the lack of details.
But here are the real scenario, I'm sorry if I didn't explain well my question.
From my CSV file, I have a raw data, then I will upload using my upload() function in into my phpmyadmin database with the table name "tbldumpbio",
See the table structure below:(tbldumpbio)
From my table tbldumpbio data, I have a function called processTimesheet()
Here's the code:
public function processTimesheet(){
$this->load->model('dbquery');
$query = $this->db->query("SELECT * FROM tbldumpbio");
foreach ($query->result() as $row){
$dateTimeExplArr = explode(' ', $row->datetimex);
$dateStr = $dateTimeExplArr[0];
$timeStr = $dateTimeExplArr[1];
if($row->status='C/Out' and !isset($timeStr) || empty($timeStr) ){
$timeStrOut ='';
} else {
$timeStrOut = $dateTimeExplArr[1];
}
if($row->status='C/In' and !isset($timeStr) || empty($timeStr) ){
$timeStrIn ='';
} else {
$timeStrIn = $dateTimeExplArr[1];
}
$data = array(
'ID' => '',
'companyAccessID' => '',
'name' => $row->name,
'empCompID' => $row->empid,
'date' => $dateStr,
'timeIn' => $timeStrIn,
'timeOut' => $timeStrOut,
'status' => '',
'inputType' => ''
);
$this->dbquery->modInsertval('tblempbioupload',$data);
}
}
This function will add another data into another table called "tblempbioupload". But here are the results that I'm getting with:
Please see the below data:(tblempbioupload)
The problem is:
the date should not be duplicated
Time In data should be added if the status is 'C/In'
Time Out data should be added if the status is 'C/Out'
The expected result should be something like this:
The first problem I see is that you have a time expressed as 15:xx:yy PM, which is an ambiguous format, as one can write 15:xx:yy AM and that would not be a valid time.
That said, if what you want is that every time the date changes a row should be written, you should do just that: store the previous date in a variable, then when you move to the next record in the source table, you compare the date with the previous one and if they differ, then you insert the row, otherwise you simply progress reading the next bit of data.
Remember that this approach works only if you're certain that the input rows are in exact order, which means ordered by EmpCompId first and then by date and then by time; if they aren't this procedure doesn't work properly.
I would probably try another approach: if (but this is not clear from your question) only one row per empcompid and date should be present, i would do a grouping query on the source table, finding the minimum entrance time, another one to find the maximum exit date, and use both of them as a source for the insert query.
This is the scenario. I've User A that send via notification to other User B,C,D... a request to join a group. So in laravel I've created the migration and the controller to handle the notification.
This is the code of GroupController
...
foreach ($userINList as $userIN) {
$userIN = str_replace(' ', '', $userIN);
$userDBList = User::all();
foreach ($userDBList as $userDB) {
$name = $userDB->last_name . $userDB->first_name;
$name = str_replace(' ', '', $name);
if (strcmp($name, $userIN) == 0) {
$newGroup->users()->attach($userDB->id, ['role' => 'member', 'state' => 'pending']);
$notification = User::find($userIN->id);
$notification->notify(new GroupNotification($newGroup));
}
}
}
...
So in $notification I'll try to pass the id of Users that receive the invite and then I use the notify() method to send the notification, but after User A created the group and there aren't notifications to User B, C, D...
I've included the use Notifiable in group model. So what's the problem? What I've have to do.
Thanks
As far as I can tell from the code you're doing the following:
There is an array of names in the $userINList variable
You loop through each of the names in the array
Remove all spaces in the name
Retrieve every User
Loop through each User
Remove all the spaces in the User's name
Compare the 2 names
If the comparison passes then you add the User to the group and send a notification
There are quite a few improvements we can make here. For example, we already know which users you wish to notify so you do not need to fetch and compare all users.
Firstly, $userINList should either be an array of User objects or an array of User ids — an array of User objects is better. Then you can simply iterate through each one.
For example, if you have an array of ids then you could do this:
$group = Group::find(1);
$userINList = [1, 2, 3, 4];
User::whereIn('id', $userINList)
->get()
->each(function ($user) use ($group) {
$group->users()->attach($user->id, [
'role' => 'member',
'state' => 'pending'
]);
$user->notify(new GroupNotification($group));
});
And if you had an array of objects it would be even easier, you could do this:
$group = Group::find(1);
collect($users)->each(function ($user) use ($group) {
$group->users()->attach($user->id, [
'role' => 'member',
'state' => 'pending'
]);
$user->notify(new GroupNotification($group));
});
Super simple :-)
I am developing a simple task management web application using laravel. The requirement states that we need to save the general information such as TaskDate, AssignedTo in a taskinfo table. List of tasks for one specific person are saved in another table called tasks. The tasks table has TaskDetailID (PK), TaskID (FK from the above table), TaskDescription, HoursRequired, etc...
The form allows users to add as many rows as they can which means a person could get assigned unlimited amount of tasks.
My problem now is saving the tasks data in the table. I've successfully saved the data for the taskinfo table, and i can even save the data for the table but only when it's one column.
Here is my store function on TaskInfoController
public function store(Request $request)
{
$validator = Validator::make(
$request->all(),
[
'TaskDate.*' => 'required',
'AssignedTo.*' => 'required',
]
,
[
'TaskDate.*.required' => 'Task Date is required.',
'AssignedTo.*.required' => 'Please assign the task to someone.',
]
);
if ($validator->fails())
{
//redirect errors to mars
}
$taskinfo = new TaskInfo();
$taskinfo->TaskDate = Carbon::createFromFormat("m/d/Y", $request->input('TaskDate'));
$taskinfo->TaskAssignedTo = $request->input('TaskAssignedTo');
// Some more columns here
$taskinfo->Save();
// Now for the tasks table
$tasksbulkinsert = array();
foreach ($request->input('TaskDescription') as $taskdescription)
{
$tasksbulkinsert[] = array('TaskID' => Uuid::uuid4(), 'TaskDescription' => $taskdescription);
}
Task::insert($tasksbulkinsert);
return redirect()->action('TaskInfoController#index')->with('flash_message', 'Successfully Saved!');}
The above code actually works perfectly but i don't know how i can insert the HoursRequired, or any additonal value with the corresponding taskdescription on the tasks table.
I tried a few approaches
having an incremental count such as i++ to know which row index (so to speak) of the taskdescription the query is currently procession, and having another foreach loop with it's own counter for the hoursrequired input and getting the value where the taskdescription's counters is equal to the hoursrequired counter. But it didn't work and even if it did, i don't think having multiple foreach loops for every column is good for performance.
Having different arrays with their own foreach loop to get the values from the inputs and then somehow merge the arrays.
Here is my HTML form
<input class="form-control" name="TaskDescription[]" type="text">
<input class="form-control" name="HoursRequired[]" type="text">
Main Question.
How can I save the TaskDescription and the HoursRequired into the database with one query.
Not so important question
The array validation at the top works but is there a way to have an error message that states the row of the error.
For example, Date is required for row number n.
You can simply:
foreach ($request->input('TaskDescription') as $i=>$taskdescription)
{
$tasksbulkinsert[] = array(
'TaskID' => Uuid::uuid4(),
'TaskDescription' => $taskdescription,
'HoursRequired' => $request->input('HoursRequired')[$i]
);
}
For your second question:
$messages = [];
foreach($this->request->get('TaskDescription') as $key => $val)
{
$messages['TaskDescription.'.$key.'.required'] = 'TD is required for row $key';
$messages['some_field.'.$key.'.some_rule'] = 'some_field custom message on row $key';
}
$validator = Validator::make(
$request->all(),
[
'TaskDate.*' => 'required',
'AssignedTo.*' => 'required',
]
,
$messages;
So I am making a quiz in which the user gets confronted with a succession of questions which he answers through a form.
The series of problems each contain a given number of questions, and the questions get asked one after the other when the user validates.
I am therefore trying to re-render the view with the form for each problem until they're all done. This is my action:
public function actionAnswer($id_serie)
{
if ($id_serie != 0) //getting the serie's info
{
$serie = Serie::find()
->where(['id' => $id_serie])
->one();
$problems = (new \yii\db\Query()) //getting the problems in the serie
->select('*')
->from('problems')
->where(['id_serie' => $id_serie])
->all();
$prob_counter = $serie->nbr_of_problems; //counts the number of questions answered
$id_serie = 0;
}
$model = new Answer;
if ($model->load(Yii::$app->request->post()) && $model->validate())
{
$model->save(); // works just fine every time
if (--$prob_counter <= 0)
{
return $this->redirect('index.php?r=student/entry');
}
}
return $this->render('answer',
['model' => $model,
'problems' => $problems,
'serie' => $serie,
'prob_counter' => $prob_counter, //these last two are for debug
'id_serie' => $id_serie]);
}
When this action gets executed the first time, $id_serie is never null or =0. Hence I am using this to query the db only once and set a counter to the total number of problems in the serie. (id est the number of time the user has to submit the form)
If his answer is valid, I decrement my counter and if it falls under 0, there are no questions to answer anymore and the user gets redirected.
However, this counter never goes down to 0: it is set correctly, it is decremented only once, and then it never falls lower, no matter where I put the line. (inside or outside any loop)
On the other hand the data from the form is properly inserted in the db each time.
What am I getting wrong?
As per your code, $prob_counter just stores the number of problems for each series. You need to change this to show the number of unanswered problems for the series. How you implement this will depend on your models and database but it should be something like:
$problems = (new \yii\db\Query()) //getting the problems in the serie
->select('*')
->from('problems')
->where(['id_serie' => $id_serie])
->andWhere('not exists (select id from answer where problemid = problems.id')
->all();
Also you should probably look at working with relational data and avoid using Query() in the above section.