Laravel events firing multiple times, should only be firining once - php

I have a model called Project, that has some model events setup like this,
class Project extends Eloquent {
public static function boot()
{
parent::boot();
// new project created
static::created(function($project)
{
// create the data informing the client to pull the new project information
$pullData = [
'id' => $project->id,
'item' => 'project',
'type' => 'created',
'interface' => '/project/basic/'.$project->id
];
foreach($project->organisation->users as $user) {
if ($user->id != $project->user_id)
Pusherer::trigger('private-user'.$user->id, 'project:createdPull', $pullData);
}
}
Event::subscribe(new ProjectEventHandler);
Event::fire('project.create', array($project));
Event::fire('project.create', array($project));
Event::forget('project.create');
Event::subscribe(new OrganisationEventHandler);
Event::fire('organisation.project.add', array($project, $project->organisation, $project->user));
Event::forget('organisation.project.add');
});
}
I create a project like this,
$project = new Project;
$project->name = $this->first_name . "'s example project";
$project->slug = Stringhelpers::_uriHash( time() . $project->name );
$project->uri_hash = Stringhelpers::_slugify($project->name);
$project->total_cost = "1000.00";
$project->start_date = date('Y-m-d H:i:s', time());
$project->finish_date = date('Y-m-d H:i:s', strtotime('+1 month'));
//$project->owner_id = $this->id;
$project->client_id = $client->id;
$project->status = 2;
$project->user_id = $this->id;
$project->organisation_id = $organisation->id;
$project->archived_at = "0000-00-00 00:00:00";
//$project->socketId = $this->socketId;
//Add the created user to the project as a just watcher.
//$project->flushEventListeners();
if($project->save()) {
$project->users()->attach(array($this->id => array('role' => 3, 'last_tab' => 'home')));
}
To my mind this this should fire the created event on the model once, and each of the subscribed events once, but it is firining the events, or the created event 3 times, would there be reason for this?
======= Further Details ======
I am saving multiple models in my process, I have noticed that I get multiple events firing when there have been a previous save, so I do the following, I get too Item events,
$project = new Project;
$project->name = $this->first_name . "'s example project";
$project->slug = Stringhelpers::_uriHash( time() . $project->name );
$project->uri_hash = Stringhelpers::_slugify($project->name);
$project->total_cost = "1000.00";
$project->start_date = date('Y-m-d H:i:s', time());
$project->finish_date = date('Y-m-d H:i:s', strtotime('+1 month'));
//$project->owner_id = $this->id;
$project->client_id = $client->id;
$project->status = 2;
$project->user_id = $this->id;
$project->organisation_id = $organisation->id;
$project->archived_at = "0000-00-00 00:00:00";
//$project->socketId = $this->socketId;
//Add the created user to the project as a just watcher.
//$project->flushEventListeners();
if($project->save()) {
$project->users()->attach(array($this->id => array('role' => 3, 'last_tab' => 'home')));
}
$item = new Item;
$item->name = $this->first_name . "'s example item";
$item->percentage_complete = 0;
$item->project_id = $project->id;
$item->workflow_id = 0;
$item->user_id = $this->id;
$item->start_date = $project->start_date;
$item->finish_date = $project->finish_date;
//$item->socketId = $this->socketId;
//$item->flushEventListeners();
$item->save();
my Item model looks like this,
public class Item extends Eloquent {
public static function boot() {
parent::boot();
// new item created
static::created(function($item) {
// fire project pusher events
Pusherer::trigger('project_'.$item->project_id, 'item:created', $item, $item->socketId);
Log::info("ITEM CREATE CALLBACK STARTED");
// fire off notifications and activity feeds
Event::subscribe( new ProjectEventHandler );
Event::fire('item.create', $item);
// Event::forget('item.created');
Log::info("ITEM CREATE CALLBACK FINISHED");
});
}
}
Why would it aggrege my events each time I save something?

Related

Laravel 9 Failing to update data in DB

I am building a blog using Laravel 9 and my update method for some unknown reason fails to update
My code Samples
Model
class Anime extends Model
{
use HasFactory;
protected $table = 'anime';
protected $primaryKey = 'id';
protected $fillable = ['anime_title','user_id','blog_title','description','slug','anime_image_profile'];
public function blogInformation() {
return $this->hasMany(BlogInfo::class);
}
public function getRouteKeyName()
{
return 'slug';
}
// protected $hidden = 'id';
}
Controller
public function update(ValidateAnimeBlogRequest $request, $id)
{
$request->validated();
/*Update the details in the database by ID*/
$update_data = Anime::findOrFail($id);
$update_data = new Anime;
$update_data->anime_title = $request->input('anime_title');
$update_data->blog_title = $request->input('blog_title');
$update_data->user_id = auth()->user()->id;
$update_data->description = $request->input('description');
$update_data->slug = Str::slug($request->input('blog_title'));
/*Check if the user also wanted to update the image*/
if($request->hasFile('anime_image_profile')) {
$path_to_images = 'images/anime_image_profile/' . $update_data->anime_image_profile;
if(File::exists($path_to_images)) {
File::delete($path_to_images);
}
$new_file_name = '9anime' . '-' . time() . '-' . $request->name . '.' . $request->anime_image_profile->extension();
$request->anime_image_profile->move(public_path('images/anime_image_profile'), $new_file_name);
$update_data->anime_image_profile = $new_file_name;
}
if($update_data->update()) {
redirect('/');
}
dd('Error');
}
ValidateAnimeBlogRequest
public function rules()
{
return [
'anime_title' => 'required | min:2', new nameRegex,
'blog_title' => ['required','min:5', new nameRegex],
'description' => ['required','min:1000'],
'premiered' => ['required'],
'genre' => ['required', new nameRegex],
'licensors' => ['required', new nameRegex],
'studio' => ['required', new nameRegex],
'anime_image_profile' => 'required | mimes:jpeg,jpg,png | max:5408'
];
}
My blade file
<form enctype="multipart/form-data" autocomplete="off" action="/blog/{{$anime['id']}}" method="POST">
#method('PUT')
#csrf
I set up a custom check just in case
if($update_data->update()) {
redirect('/');
}
dd('Error');
The output on my webpage from this is "Error" // app\Http\Controllers\AnimeController.php:156
And when I dd($update_data) I see that the data has been updated yet it does not get sent to the database.
I tried replacing $update_data->update() with $update_data->save() but that now creates new data in the DB instead of updating the existing one
You can keep it as the save() method. Just update the lines above where you are creating a new Anime() instance to only be created if the record cannot be found via $id from the line above.
public function update(ValidateAnimeBlogRequest $request, $id)
{
$request->validated();
/*Update the details in the database by ID*/
$update_data = Anime::findOrFail($id);
if(!$update_data) {
$update_data = new Anime;
}
$update_data->anime_title = $request->input('anime_title');
$update_data->blog_title = $request->input('blog_title');
$update_data->user_id = auth()->user()->id;
$update_data->description = $request->input('description');
$update_data->slug = Str::slug($request->input('blog_title'));
/*Check if the user also wanted to update the image*/
if($request->hasFile('anime_image_profile')) {
$path_to_images = 'images/anime_image_profile/' . $update_data->anime_image_profile;
if(File::exists($path_to_images)) {
File::delete($path_to_images);
}
$new_file_name = '9anime' . '-' . time() . '-' . $request->name . '.' . $request->anime_image_profile->extension();
$request->anime_image_profile->move(public_path('images/anime_image_profile'), $new_file_name);
$update_data->anime_image_profile = $new_file_name;
}
if($update_data->save()) {
redirect('/');
}
dd('Error');
}
This will create a new instance only if a record is not found and won't give a new db record

how to condition same time data to insert and update in laravel

I will try If on today's date, any row insert Then update value use id
And if not any row today's date Then insert a new row. but the problem is update value is working but not insert a new row message is "Trying to get property 'Date' of non-object"
public function present($id){
$user = User::find($id);
$date = date('Y-m-d');
$id = $user->id;
$Attendance= Attendance::where('User_A_ID','=',$id)->first();
if($Attendance->Date == $date){
$Attendance->A_sts = '0';
$Attendance->Date = $date;
$Attendance->User_A_ID = $user->id;
$Attendance->save();
} else {
$Attendance= new Attendance();
$Attendance->A_sts = '0';
$Attendance->Date = $date;
$Attendance->User_A_ID = $user->id;
$Attendance->save();
}
return back();
}
If I understand you correctly you want to update the existing attendance that belongs to the user or create a new attendance if the user does not have one.
You can simplify your code:
public function present($id)
{
$user = User::findOrFail($id);
$date = date('Y-m-d');
$Attendance = Attendance::firstOrNew(['User_A_ID' => $user->id, 'Date', $date]);
$Attendance->User_A_ID = $user->id;
$Attendance->Date = $date;
$Attendance->A_sts = '0';
$Attendance->save();
return back();
}
Use findOrFail to check if the user exists, and then use firstOrNew to retrieve the existing attendance for today or create a new instance of it, this way you can get rid of your if statement.

Calling a controller create() method for multiple objects from one controller

I am new to Laravel so here goes:
I am creating a page for a user to register an account for his company and enter the payment method and her own login credentials. So this one page will create a Company, a User and a Payment record. The page calls the RegisterController method in Laravel.
However, each of the three objects' create methods are defined within their own controllers. Is it valid (or even good practice) to call other controller methods from within a controller like so (some boilerplate code is missing below...I just want to get my point across)
class RegisterController extends Controller {
//boilerplate code.....
protected function create(Request $request) {
//Create the company
$objCompany = CompanyController->create($request); //gets created first since it is 'top level'
//now create the user:
$objUser = UserController->create($request,$objCompany->id); //Company ID is FK in User
//Create the paymentMethod
$objPaymentMethod = PaymentMethodController->create($request,$objCompany->id); //Company ID is FK in PaymentMethod
}//end create()
}
See rather than calling distinct controllers for table creation try to store the data directly to distinct tables from one controller itself.
like this
public function store(Request $request)
{
$data = request()->validate([
...
]);
$users = new User;
$users->name = $request->name;
$users->ltname = $request->lname;
$users->dob = $request->dob;
$users->bgroup = $request->bgroup;
$users->email = $request->email;
$users->phone = $request->phone;
$users->district = $request->district;
$users->country = $request->country;
$users->state = $request->state;
$users->city = $request->city;
$users->add = $request->add;
$users->pincode = $request->pincode;
$users->password = Hash::make(request('password'));
$users->type = 'Service Center';
$users->save();
$posts = new Post;
$posts->user_id = $users->id;
$posts->fname = $request->fname;
$posts->phone1 = $request->phone1;
$posts->email1 = $request->email1;
$posts->add2 = $request->add2;
$posts->pincode1 = $request->pincode1;
$posts->district1 = $request->district1;
$posts->city1 = $request->city1;
$posts->state1 = $request->state1;
$posts->country1 = $request->country1;
$posts->cmp_id = $request->cmp_id . $users->id;
$posts->gst = $request->gst;
$posts->badd = $request->badd;
$posts->badd2 = $request->badd2;
$posts->save();
$subscribe = new Subscription;
$subscribe->user_id = $users->id;
$subscribe->post_id = $posts->id;
$subscribe->offer_id = $request->m_option_1;
$subscribe->trial_start_date = $offers->of_start_date;
$subscribe->trial_end_date = $offers->of_end_date;
$subscribe->pck_name = $offers->pck_id;
$subscribe->valid_to = $offers->validity;
$subscribe->type = $offers->of_type;
$subscribe->save();
$transactions = new Transactions;
$transactions->user_id = $users->id;
$transactions->post_id = $posts->id;
$transactions->ddl_payment = $request->ddl_payment;
$transactions->txt_ac = $request->txt_ac;
$transactions->txt_amount = $request->txt_amount;
$transactions->txt_dat = $request->txt_dat;
$transactions->txt_ref = $request->txt_ref;
$transactions->save();
return redirect('/show');
}
Here the data which I'm accepting from the form is been stored in four tables users, post, subscription & transaction.

Laravel collection map function overrides default collection

I have a code where I use map to create a new collection of high scores.
The problem I have is that it overrides the default user collections. Which is not my intention.
Here is the code
$users = Users::all();
$highscore = $users->map(
function ($user) {
$calls = $user->calls->filter(
function ($call) {
$date = Carbon::parse($call->datetime)->format("Y-m-d");
$today = Carbon::now()->format("Y-m-d");
return $date == $today;
}
);
return [
'id' => $user->id,
'duration' => $calls->sum('duration'),
];
}
);
If i dump the first user after getting all the users I get the first user. Like this.
$users = Users::all();
dd($users->first());
If I dump the first user after the high score map. I get all Calls from that user which is another model. Which means that the users collection has been modified. Like this.
$highscore = $users->map(
function ($user) {
$calls = $user->calls->filter(
function ($call) {
$date = Carbon::parse($call->datetime)->format("Y-m-d");
$today = Carbon::now()->format("Y-m-d");
return $date == $today;
}
);
return [
'id' => $user->id,
'duration' => $calls->sum('duration'),
];
}
);
dd($users->first()):
Any idea on how to handle this behaviour?
The map function returns an array of [[$userId => $duration], ...]. What you want to do is to order your users by the sum of their calls.
I believe that, in order to do that easily, you should add to your User model:
public function getTodayCallSum() {
return $user->calls->filter(function($call) {
$date = Carbon::parse($call->datetime)->format("Y-m-d");
$today = Carbon::now()->format("Y-m-d");
return $date == $today;
})->sum('duration');
}
And then edit your query:
$users = User::all();
$firstUser = $users->sortBy('todayCallSum')->first();
I haven't tested this code, but I think it should help you towards the right direction.

Getting emails after a specific date with php-ews (Exchange Web Services)

In my PHP script, I need to figure out how to retrieve all emails that are either after a specified message ID or after a specific date (Either will work, I just need to retrieve emails that are new since the last time I scraped the inbox).
This inbox is getting thousands of emails a day, and I can't delete any emails for 30 days. For the initial import I was just doing an offset from the beginning of the inbox, but obviously that won't work once we start cleaning out emails.
I think I have to set the $Restriction property of the class "EWSType_FindItemType", but I don't think the necessary classes exist in php-ews for me to do this. I've tried to add them myself, but I don't understand enough about EWS or SOAP.
So far the only thing I've come up with is this:
$Request->Restriction = new EWSType_RestrictionType();
$Request->Restriction->IsGreaterThan = new stdClass;
$Request->Restriction->IsGreaterThan->FieldURIOrConstant = new stdClass;
$Request->Restriction->IsGreaterThan->FieldURIOrConstant->Constant = '2012-01-02T07:04:00Z';
$Request->Restriction->IsGreaterThan->FieldURI = new stdClass;
$Request->Restriction->IsGreaterThan->FieldURI->FieldURI = 'item:DateTimeReceived';
And that doesn't work :(
Here's the code I am currently using to retrieve email:
<?php
require( dirname( __FILE__ ) . '/ews/ExchangeWebServicesLoader.php' );
$ews = new ExchangeWebServices( EXCHANGE_HOSTNAME, EXCHANGE_USERNAME, EXCHANGE_PASSWORD, ExchangeWebServices::VERSION_2010_SP1 );
$Request = new EWSType_FindItemType();
$Request->ItemShape = new EWSType_ItemResponseShapeType();
$Request->ItemShape->BaseShape = EWSType_DefaultShapeNamesType::ALL_PROPERTIES;
$Request->ItemShape->BodyType = EWSType_BodyTypeResponseType::TEXT;
$Request->ItemShape->BodyTypeSpecified = true;
$Request->Traversal = EWSType_ItemQueryTraversalType::SHALLOW;
$Request->IndexedPageItemView = new EWSType_IndexedPageViewType();
$Request->IndexedPageItemView->MaxEntriesReturned = 25;
$Request->IndexedPageItemView->BasePoint = 'Beginning';
$Request->IndexedPageItemView->Offset = $offset;
$Request->ParentFolderIds = new EWSType_NonEmptyArrayOfBaseFolderIdsType();
$Request->ParentFolderIds->DistinguishedFolderId = new EWSType_DistinguishedFolderIdType();
$Request->ParentFolderIds->DistinguishedFolderId->Id = 'inbox';
$Request->ParentFolderIds->DistinguishedFolderId->Mailbox = new EWSType_EmailAddressType();
$Request->ParentFolderIds->DistinguishedFolderId->Mailbox->EmailAddress = 'sharedmailbox#company.org';
// sort order
$Request->SortOrder = new EWSType_NonEmptyArrayOfFieldOrdersType();
$Request->SortOrder->FieldOrder = array();
$order = new EWSType_FieldOrderType();
$order->FieldURI = new stdClass;
$order->FieldURI->FieldURI = 'item:DateTimeReceived';
$order->Order = 'Ascending';
$Request->SortOrder->FieldOrder[] = $order;
$response = $ews->FindItem($Request);
$items = $response->ResponseMessages->FindItemResponseMessage->RootFolder->Items->Message;
foreach ( $items as $item ) {
// Do stuff
}
Any help would be greatly appreciated!
Restriction are tricky in EWS, true. You can take a look at haw they are used in EWSWrapper, here's example how to create AND restriction to get items in between date range:
//create AND restrction
$request->Restriction = new EWSType_RestrictionType();
$request->Restriction->And = new EWSType_AndType();
$request->Restriction->And->IsGreaterThanOrEqualTo = new EWSType_IsGreaterThanOrEqualToType();
$request->Restriction->And->IsGreaterThanOrEqualTo->ExtendedFieldURI = new EWSType_PathToExtendedFieldType;
$request->Restriction->And->IsGreaterThanOrEqualTo->ExtendedFieldURI->DistinguishedPropertySetId = "Task";
$request->Restriction->And->IsGreaterThanOrEqualTo->ExtendedFieldURI->PropertyId = "33029";
$request->Restriction->And->IsGreaterThanOrEqualTo->ExtendedFieldURI->PropertyType = "SystemTime";
$request->Restriction->And->IsGreaterThanOrEqualTo->FieldURIOrConstant->Constant->Value = date('c', $start);
$request->Restriction->And->IsLessThanOrEqualTo = new EWSType_IsLessThanOrEqualToType();
$request->Restriction->And->IsLessThanOrEqualTo->ExtendedFieldURI = new EWSType_PathToExtendedFieldType;
$request->Restriction->And->IsLessThanOrEqualTo->ExtendedFieldURI->DistinguishedPropertySetId = "Task";
$request->Restriction->And->IsLessThanOrEqualTo->ExtendedFieldURI->PropertyId = "33029";
$request->Restriction->And->IsLessThanOrEqualTo->ExtendedFieldURI->PropertyType = "SystemTime";
$request->Restriction->And->IsLessThanOrEqualTo->FieldURIOrConstant->Constant->Value = date('c', $end);
And the types used:
class EWSType_RestrictionType extends EWSType {
/**
* SearchExpression property
*
* #var EWSType_SearchExpressionType
*/
public $SearchExpression;
/**
* Constructor
*/
public function __construct() {
$this->schema = array(
array(
'name' => 'SearchExpression',
'required' => false,
'type' => 'SearchExpressionType',
),
); // end $this->schema
} // end function __construct()
} // end class RestrictionType
<?php
class EWSType_AndType extends EWSType {
/**
* SearchExpression property
*
* #var EWSType_MultipleOperandBooleanExpressionType
*/
public $SearchExpression;
/**
* Constructor
*/
public function __construct() {
$this->schema = array(
array(
'name' => 'SearchExpression',
'required' => false,
'type' => 'MultipleOperandBooleanExpressionType',
),
); // end $this->schema
} // end function __construct()
} // end class AndType
class EWSType_IsLessThanOrEqualToType extends EWSType {
/**
* SearchExpression property
*
* #var EWSType_TwoOperandExpressionType
*/
public $SearchExpression;
/**
* Constructor
*/
public function __construct() {
$this->schema = array(
array(
'name' => 'SearchExpression',
'required' => false,
'type' => 'TwoOperandExpressionType',
),
); // end $this->schema
} // end function __construct()
} // end class IsLessThanOrEqualToType
class EWSType_IsGreaterThanOrEqualToType extends EWSType {
/**
* SearchExpression property
*
* #var EWSType_TwoOperandExpressionType
*/
public $SearchExpression;
/**
* Constructor
*/
public function __construct() {
$this->schema = array(
array(
'name' => 'SearchExpression',
'required' => false,
'type' => 'TwoOperandExpressionType',
),
); // end $this->schema
} // end function __construct()
} // end class IsGreaterThanOrEqualToType
Thanks Maiiku for your code samples!
This is how I enabled filtering by date and subject field using the PHP Exchange Web Services library (php-ews).
(You will need to require_once the relevant EWSType libraries first before using this sample).
$start = new DateTime('2013-03-31');
$Request->Restriction = new EWSType_RestrictionType();
$Request->Restriction->And = new EWSType_AndType();
$Request->Restriction->And->IsGreaterThanOrEqualTo = new EWSType_IsGreaterThanOrEqualToType();
$Request->Restriction->And->IsGreaterThanOrEqualTo->FieldURI = new stdClass;
$Request->Restriction->And->IsGreaterThanOrEqualTo->FieldURI->FieldURI = 'item:DateTimeReceived';
$Request->Restriction->And->IsGreaterThanOrEqualTo->FieldURIOrConstant->Constant->Value = $start->format('c');
$Request->Restriction->And->Contains = new EWSType_ContainsExpressionType();
$Request->Restriction->And->Contains->FieldURI = new stdClass;
$Request->Restriction->And->Contains->FieldURI->FieldURI = 'item:Subject';
$Request->Restriction->And->Contains->Constant->Value = 'annual leave application';
$Request->Restriction->And->Contains->ContainmentMode = 'Substring';
$Request->Restriction->And->Contains->ContainmentComparison = 'Exact';
Hope this helps!

Categories