Ticket1052 : Definetly fix Contact stat issues

- correction on table : PK is now Date instead of Timestamp
- Contact::incrementStat now dispatches a job to handle concurent incrementations
- Transactions are used on contact actions
- date is no correctly casted
- isoFormat is use with Carbon
This commit is contained in:
Jérémy Munsch 2022-09-29 11:39:25 +02:00
parent b46aa0b60e
commit 191b83780a
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
4 changed files with 173 additions and 33 deletions

View file

@ -11,6 +11,7 @@ use App\Jobs\SendPasserelleMail;
use App\Models\Contact as Contact;
use App\Models\MailContactData;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ContactController extends Controller
@ -39,19 +40,21 @@ class ContactController extends Controller
*/
public function contactSubmit(ContactFormRequest $request)
{
try {
$data = new MailContactData($request->all());
if ($request->type == ContactFormType::onepage()->value) {
SendNLMail::dispatchAfterResponse($data);
} else {
SendArchadiaMail::dispatchAfterResponse($data);
return DB::transaction(function () use ($request) {
try {
$data = new MailContactData($request->all());
if ($request->type == ContactFormType::onepage()->value) {
SendNLMail::dispatchAfterResponse($data);
} else {
SendArchadiaMail::dispatchAfterResponse($data);
}
Contact::incrementStat($data->type);
return back()->with('status', true);
} catch (AbortContactMailer $e) {
Log::error('Fail to build mail from contact page ', ['exception' => $e]);
}
Contact::incrementStat($data->type);
return back()->with('status', true);
} catch (AbortContactMailer $e) {
Log::error('Fail to build mail from contact page ', ['exception' => $e]);
}
return back()->withInput()->with('status', false);
return back()->withInput()->with('status', false);
});
}
/**
@ -73,15 +76,17 @@ class ContactController extends Controller
*/
public function complainSubmit(ContactFormRequest $request)
{
try {
$data = new MailContactData($request->all());
SendPasserelleMail::dispatchAfterResponse($data);
Contact::incrementStat($data->type);
return back()->with('status', true);
} catch (AbortContactMailer $e) {
Log::error('Fail to build mail from complain page ', ['exception' => $e]);
}
return back()->withInput()->with('status', false);
return DB::transaction(function () use ($request) {
try {
$data = new MailContactData($request->all());
SendPasserelleMail::dispatchAfterResponse($data);
Contact::incrementStat($data->type);
return back()->with('status', true);
} catch (AbortContactMailer $e) {
Log::error('Fail to build mail from complain page ', ['exception' => $e]);
}
return back()->withInput()->with('status', false);
});
}
/**

View file

@ -0,0 +1,59 @@
<?php
namespace App\Jobs;
use App\Enums\ContactFormType;
use App\Models\Contact;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Carbon;
class IncrementContactStatsJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/**
* @var \App\Enums\ContactFormType
*/
private $type;
/**
* Create a new job instance.
*
* @param \App\Enums\ContactFormType $type
* @return void
*/
public function __construct(ContactFormType $type)
{
$this->type = $type;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$insert = function (ContactFormType $type) {
$contact = Contact::where('date', '=', Carbon::now()->isoFormat('YYYY-MM-DD'))->firstOrNew();
// * Can't do $contact->counter[$type->value]++ => ErrorException
$d = $contact->values;
$d[$type->value]++;
$contact->values = $d;
$contact->saveOrFail();
};
try {
$insert($this->type);
} catch (\Exception $e) {
// * Handle user requests colisions
$insert($this->type);
}
}
}

View file

@ -3,7 +3,9 @@
namespace App\Models;
use App\Enums\ContactFormType;
use App\Jobs\IncrementContactStatsJob;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
class Contact extends Model
{
@ -21,6 +23,15 @@ class Contact extends Model
*/
public $timestamps = false;
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'date' => 'date'
];
/**
* Perform any actions required after the model boots.
*
@ -28,14 +39,13 @@ class Contact extends Model
*/
protected static function booted()
{
static::creating(function ($contact) {
$contact->date = date('Y-m-d');
return $contact;
static::creating(function (self $model) {
$model->date = Carbon::now();
$model->all = 1;
});
static::updating(function ($contact) {
static::updating(function (self $model) {
// * increment general counter
$contact->all++;
return $contact;
$model->all = $model->all + 1;
});
}
@ -47,12 +57,7 @@ class Contact extends Model
*/
public static function incrementStat(ContactFormType $type)
{
$contact = static::where('date', '=', date('Y-m-d'))->firstOrNew();
// * Can't do $contact->counter[$type->value]++ => ErrorException
$d = $contact->values;
$d[$type->value]++;
$contact->values = $d;
$contact->save();
IncrementContactStatsJob::dispatch($type);
}
/**

View file

@ -0,0 +1,71 @@
<?php
use App\Models\Contact;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class ContactsFixPkShouldBeDateNotTimestamp extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// * Merge contacts by Day
$filteredContacts = collect();
Contact::all()->each(function (Contact $contactStat) use ($filteredContacts) {
$dateString = Carbon::parse($contactStat->date)->isoFormat('YYYY-MM-DD');
if ($filteredContacts->has($dateString)) {
/** @var \App\Models\Contact */
$uniqueContactStat = $filteredContacts->get($dateString);
$uniqueContactStat->values = collect($uniqueContactStat->values)
->mapWithKeys(function (int $counter, int $ContactFormTypeValue) use ($contactStat) {
return [
$ContactFormTypeValue => $counter + $contactStat->values[$ContactFormTypeValue]
];
})->all();
return;
}
$filteredContacts->put($dateString, $contactStat);
});
// * Correct All counter
$filteredContacts = $filteredContacts->map(function (Contact $contactStat) {
$contactStat->all = collect($contactStat->values)->reduce(function ($previous, int $next) {
return intval($previous) + $next;
});
return $contactStat;
});
DB::transaction(function () use ($filteredContacts) {
DB::table('contacts')->truncate();
Schema::table('contacts', function (Blueprint $table) {
$table->date('date')->nullable(false)->default(null)->change();
});
});
$filteredContacts->each(function (Contact $contactStat) {
// * Force Insert.
DB::table('contacts')->insert([
'date' => $contactStat->date->isoFormat('YYYY-MM-DD'),
'all' => $contactStat->all,
'values' => serialize($contactStat->values),
]);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('contacts', function (Blueprint $table) {
$table->timestamp('date')->nullable(false)->useCurrent()->change();
});
}
}