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 :-)
Related
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.
There's plenty of other questions surrounding this where people were using it wrong, but I'm confident I have the proper syntax and it's just leaving out the search term by the time it makes it to sql.
I've done 2 iterations. One where I add the 'sub' again and one where I don't
protected function upsertUser( $profile ) {
$sub = $profile['sub'];
$email = $profile['email'];
$name = $profile['name'];
Log::stack(['single'])->critical(compact("sub", "email", "name"));
return User::firstOrCreate(compact("sub"), ['sub' => $profile['sub'], 'email' => $profile['email'] ?? '', 'name' => $profile['name'] ?? '']);
}
protected function upsertUser( $profile ) {
$sub = $profile['sub'];
$email = $profile['email'];
$name = $profile['name'];
Log::stack(['single'])->critical(compact("sub", "email", "name"));
return User::firstOrCreate(compact("sub"), ['email' => $profile['email'] ?? '', 'name' => $profile['name'] ?? '']);
}
In both cases, sub just doesn't make it to the creation failing with:
A message showing that it doesn't even try to insert 'sub'
Logging the array shows that it contains the desired data.
Now I had come across a post where someone suggested that it was mass assignment protection stopping me from adding sub. I don't think it's related, but I tried to add
protected $fillable = [
'name',
'email',
'sub',
];
To the users model but it did not help. I do not want to allow nullable, because I need that data.
So this was terrible. I'm following along with this Auth0 sample app guide and in this sample they have a users model. I think this is default to laravel.
App/models/users != App/user
I had to make sub fillable on that "model" it's not even a traditional model it extends Auth\User
For example, I have an auction where Users can bid on items.
When a user creates a new bid I want to be sure that this user didn't bid on this item before.
The BID table structure is id, item_id, user_id, price. So basically I need to check if there is a record that has same item_id and same user_id at the same time.
Is there any way to do this using Laravels Validator?
$validator = Validator::make($request->all(), [
'item_id' => 'unique:item_id,user_id,NULL,id,'. auth()->user()->id,
'price' => 'required|integer'
]);
The error I have:
Undefined offset: 1
According to Laravel docs:
[
'item_id' => 'unique:bids,item_id,null,id,user_id,' . auth()->id(),
]
Create a model for Users table.
Now,
$itemIds= User:: where ('user_id',$userId)->pluck('item_Id');
foreach($itemIds as $itemId){
if ($itemId == $newItemId) {
// write code if item id is already present
}
else {
// write code if item id is not present
}
}
you can use laravel unique validation as
'user_id' => 'unique:item_id,user_id,NULL,id,'.$user->id
is it possible to save more data than just the id's to a many-to-many pivot?
My Code:
public function lists() {
return $this->belongsToMany('ShoppingList','shopping_list_ingredients','shopping_list_id','ingredients_id')
->withPivot(array('unit','amount'))
->withTimestamps();
}
and vice verca!
And now, I need to add the additional data to the pivot.
This is my saving code:
$list = new ShoppingList;
$list->user_id = Auth::user()->id;
$list->title = Input::get('recipe_title');
$list->save();
$list->ingredients()->sync(Input::get('ingredient'));
$list->push();
and my view code:
- {{$i->amount}} {{$i->unit}} {{$i->name}} - {{ Form::checkbox('ingredient[]', $i->id) }}<br/>
Now I need somehow pass the "amount" and "unit" for each ID into the controller and into the pivot. Right now, it only saves the IDs.
How can I do it?
You have to use the attach function.
$list->ingredients()->attach($ingredients->id,['unit' => $unit, 'amount' => $amount]);
You may try something like this:
$ingredientId = Input::get('ingredient');
$amount = 'some amount';
$unit = 'some unit';
$pivotData = array($ingredientId => array('amount' => $amount, 'unit' => $unit));
$list->ingredients()->sync($pivotData);
You may also use attach method, read the documentation on Laravel Website for more information.
I'm using CakePHP 1.3.8, and I've installed the CakeDC Search plugin. I have a Tutorial model, which is in a HABTM relationship with a LearningGoal model.
I have a search action & view in the Tutorials controller with which I can successfully search fields in the Tutorial model. I'd also like to filter my tutorial search results using LearningGoal checkboxes on the same form. I've tried adding various parameters to Tutorial's $filterArgs and TutorialsController's $presetVars. I've also tried moving the relevant $filterArgs to the LearningGoal model. I have not yet been able to successfully trigger the entry for learning goals in $filterArgs.
I think I must be missing something obvious. Or maybe the Search plugin doesn't support what I'm trying to do. Does anyone know how to use this plugin to search on associated models?
So here's what I've figured out. You can combine what's below with the Search plugin directions to search on related models.
The $filterArgs piece in the Tutorial model must look like this:
var $filterArgs = array(
array('name' => 'LearningGoal', 'type' => 'subquery', 'method' => 'findByLearningGoals', 'field' => 'Tutorial.id'),
);
Here's the supporting function in the Tutorial model:
function findByLearningGoals($data = array()) {
$ids = explode('|', $data['LearningGoal']);
$ids = join(',', $ids);
$this->LearningGoalsTutorial->Behaviors->attach('Containable', array('autoFields' => false));
$this->LearningGoalsTutorial->Behaviors->attach('Search.Searchable');
$query = $this->LearningGoalsTutorial->getQuery('all',
array(
'conditions' => array('LearningGoalsTutorial.learning_goal_id IN (' . $ids . ')'),
'fields' => array('tutorial_id'),
)
);
return $query;
}
In TutorialsController, $presetVars should look like this:
public $presetVars = array(
array('field' => 'LearningGoal', 'type' => 'checkbox', 'model' => 'Tutorial'),
);
And in my search action in TutorialsController, I did this:
$this->LearningGoal = $this->Tutorial->LearningGoal;
The Prg component seems to need that.
I am using CakePHP version 2.X
Every time I come to do this in a project I always spend hours figuring out how to do it using CakeDC search behavior so I wrote this to try and remind myself with simple language what I need to do. I've also noticed that although Michael is generally correct there is no explanation which makes it more difficult to modify it to one's own project.
When you have a "has and belongs to many" relationship and you are wanting to search the joining table i.e. the table that has the two fields in it that joins the tables on either side of it together in a many-to-many relationship you want to create a subquery with a list of IDs from one of the tables in the relationship. The IDs from the table on the other side of the relationship are going to be checked to see if they are in that record and if they are then the record in the main table is going to be selected.
In this following example
SELECT Handover.id, Handover.title, Handover.description
FROM handovers AS Handover
WHERE Handover.id in
(SELECT ArosHandover.handover_id
FROM aros_handovers AS ArosHandover
WHERE ArosHandover.aro_id IN (3) AND ArosHandover.deleted != '1')
LIMIT 20
all the records from ArosHandover will be selected if they have an aro_id of 3 then the Handover.id is used to decide which Handover records to select.
On to how to do this with the CakeDC search behaviour.
Firstly, place the field into the search form:
echo $this->Form->create('Handover', array('class' => 'form-horizontal'));?>
echo $this->Form->input('aro_id', array('options' => $roles, 'multiple' => true, 'label' => __('For', true), 'div' => false, true));
etc...
notice that I have not placed the form element in the ArosHandover data space; another way of saying this is that when the form request is sent the field aro_id will be placed under the array called Handover.
In the model under the variable $filterArgs:
'aro_id' => array('name' => 'aro_id', 'type' => 'subquery', 'method' => 'findByAros', 'field' => 'Handover.id')
notice that the type is 'subquery' as I mentioned above you need to create a subquery in order to be able to find the appropriate records and by setting the type to subquery you are telling CakeDC to create a subquery snippet of SQL. The method is the function name that are going to write the code under. The field element is the name of the field which is going to appear in this part of the example query above
WHERE Handover.id in
Then you write the function that will return the subquery:
function findByAros($data = array())
{
$ids = ''; //you need to make a comma separated list of the aro_ids that are going to be checked
foreach($data['aro_id'] as $k => $v)
{
$ids .= $v . ', ';
}
if($ids != '')
{
$ids = rtrim($ids, ', ');
}
//you only need to have these two lines in if you have not already attached the behaviours in the ArosHandover model file
$this->ArosHandover->Behaviors->attach('Containable', array('autoFields' => false));
$this->ArosHandover->Behaviors->attach('Search.Searchable');
$query = $this->ArosHandover->getQuery('all',
array(
'conditions' => array('ArosHandover.aro_id IN (' . $ids . ')'),
'fields' => array('handover_id'), //the other field that you need to check against, it's the other side of the many-to-many relationship
'contain' => false //place this in if you just want to have the ArosHandover table data included
)
);
return $query;
}
In the Handovers controller:
public $components = array('Search.Prg', 'Paginator'); //you can also place this into AppController
public $presetVars = true; //using $filterArgs in the model configuration
public $paginate = array(); //declare this so that you can change it
// this is the snippet of the search form processing
public function admin_find()
{
$this->set('title_for_layout','Find handovers');
$this->Prg->commonProcess();
if(isset($this->passedArgs) && !empty($this->passedArgs))
{//the following line passes the conditions into the Paginator component
$this->Paginator->settings = array('conditions' => $this->Handover->parseCriteria($this->passedArgs));
$handovers = $this->Paginator->paginate(); // this gets the data
$this->set('handovers', $handovers); // this passes it to the template
If you want any further explanation as to why I have done something, ask and if I get an email to tell me that you have asked I will give an answer if I am able to.