Publication initiale de RelaisSMS : documentation et sources nettoyees

This commit is contained in:
2026-10-21 09:29:39 +02:00
commit ac186b03b1
158 changed files with 24818 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Model;
#[Fillable(['key', 'value', 'type'])]
class AppSetting extends Model
{
public static function string(string $key, string $default = ''): string
{
$setting = self::query()->where('key', $key)->first();
if ($setting === null) {
$configured = config('relais_sms.default_settings.'.$key);
return is_string($configured) ? $configured : $default;
}
return (string) $setting->value;
}
public static function putString(string $key, string $value): void
{
self::query()->updateOrCreate(
['key' => $key],
[
'value' => $value,
'type' => 'string',
],
);
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
#[Fillable([
'source',
'external_id',
'remote_url',
'remote_etag',
'summary',
'description',
'location',
'starts_at',
'ends_at',
'attendees',
'synced_at',
'deleted_at',
])]
class CalendarEvent extends Model
{
/**
* @return HasMany<CalendarReminder, $this>
*/
public function reminders(): HasMany
{
return $this->hasMany(CalendarReminder::class);
}
protected function casts(): array
{
return [
'starts_at' => 'datetime',
'ends_at' => 'datetime',
'attendees' => 'array',
'synced_at' => 'datetime',
'deleted_at' => 'datetime',
];
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
#[Fillable([
'calendar_event_id',
'sms_campaign_id',
'reminder_key',
'action',
'trigger_minutes',
'remind_at',
'status',
'last_error',
'sent_at',
])]
class CalendarReminder extends Model
{
/**
* @return BelongsTo<CalendarEvent, $this>
*/
public function event(): BelongsTo
{
return $this->belongsTo(CalendarEvent::class, 'calendar_event_id');
}
/**
* @return BelongsTo<SmsCampaign, $this>
*/
public function smsCampaign(): BelongsTo
{
return $this->belongsTo(SmsCampaign::class);
}
protected function casts(): array
{
return [
'remind_at' => 'datetime',
'sent_at' => 'datetime',
];
}
}
+94
View File
@@ -0,0 +1,94 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
#[Fillable([
'source',
'external_id',
'remote_url',
'remote_etag',
'first_name',
'last_name',
'organization',
'email',
'notes',
'sms_opt_out',
'is_favorite',
'synced_at',
])]
class Contact extends Model
{
use SoftDeletes;
/**
* @return HasMany<ContactPhoneNumber, $this>
*/
public function phoneNumbers(): HasMany
{
return $this->hasMany(ContactPhoneNumber::class);
}
/**
* @return BelongsToMany<ContactGroup, $this>
*/
public function groups(): BelongsToMany
{
return $this->belongsToMany(ContactGroup::class, 'contact_contact_group')
->withTimestamps();
}
public function displayName(): string
{
return trim(implode(' ', array_filter([$this->first_name, $this->last_name])));
}
/**
* @param Builder<Contact> $query
*/
#[Scope]
protected function search(Builder $query, ?string $term): void
{
$term = trim((string) $term);
if ($term === '') {
return;
}
$like = '%'.str_replace(['%', '_'], ['\%', '\_'], $term).'%';
$query->where(function (Builder $query) use ($like): void {
$query
->where('first_name', 'like', $like)
->orWhere('last_name', 'like', $like)
->orWhere('organization', 'like', $like)
->orWhere('email', 'like', $like)
->orWhereHas('phoneNumbers', function (Builder $query) use ($like): void {
$query
->where('raw_number', 'like', $like)
->orWhere('normalized_number', 'like', $like);
});
});
}
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'sms_opt_out' => 'boolean',
'is_favorite' => 'boolean',
'synced_at' => 'datetime',
];
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
#[Fillable([
'source',
'external_id',
'name',
'slug',
'description',
'is_favorite',
'synced_at',
])]
class ContactGroup extends Model
{
use SoftDeletes;
/**
* @return BelongsToMany<Contact, $this>
*/
public function contacts(): BelongsToMany
{
return $this->belongsToMany(Contact::class, 'contact_contact_group')
->withTimestamps();
}
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'is_favorite' => 'boolean',
'synced_at' => 'datetime',
];
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
#[Fillable([
'contact_id',
'label',
'raw_number',
'normalized_number',
'country',
'is_valid',
'is_mobile',
'is_primary',
'validation_error',
])]
class ContactPhoneNumber extends Model
{
public function hasDetectedMobileNumber(): bool
{
if ($this->is_mobile) {
return true;
}
$normalizedNumber = (string) $this->normalized_number;
if (preg_match('/^\+33[67]\d{8}$/', $normalizedNumber) === 1) {
return true;
}
$rawDigits = preg_replace('/\D+/', '', (string) $this->raw_number) ?? '';
return preg_match('/^0[67]\d{8}$/', $rawDigits) === 1
|| preg_match('/^33[67]\d{8}$/', $rawDigits) === 1
|| preg_match('/^0033[67]\d{8}$/', $rawDigits) === 1;
}
/**
* @return BelongsTo<Contact, $this>
*/
public function contact(): BelongsTo
{
return $this->belongsTo(Contact::class);
}
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'is_valid' => 'boolean',
'is_mobile' => 'boolean',
'is_primary' => 'boolean',
];
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
#[Fillable([
'user_id',
'provider',
'status',
'message',
'recipients_count',
'selected_contact_ids',
'selected_group_ids',
'selected_group_names',
'sent_count',
'failed_count',
'provider_service_name',
'provider_tag',
'provider_job_id',
'request_payload',
'response_payload',
'error_message',
'scheduled_at',
'sent_at',
'canceled_at',
])]
class SmsCampaign extends Model
{
/**
* @return BelongsTo<User, $this>
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* @return HasMany<SmsDelivery, $this>
*/
public function deliveries(): HasMany
{
return $this->hasMany(SmsDelivery::class);
}
protected function casts(): array
{
return [
'request_payload' => 'array',
'response_payload' => 'array',
'selected_contact_ids' => 'array',
'selected_group_ids' => 'array',
'selected_group_names' => 'array',
'scheduled_at' => 'datetime',
'sent_at' => 'datetime',
'canceled_at' => 'datetime',
];
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
#[Fillable([
'sms_campaign_id',
'contact_id',
'contact_phone_number_id',
'contact_name',
'number',
'status',
'provider_message_id',
'provider_response',
'error_message',
'sent_at',
])]
class SmsDelivery extends Model
{
/**
* @return BelongsTo<SmsCampaign, $this>
*/
public function campaign(): BelongsTo
{
return $this->belongsTo(SmsCampaign::class, 'sms_campaign_id');
}
/**
* @return BelongsTo<Contact, $this>
*/
public function contact(): BelongsTo
{
return $this->belongsTo(Contact::class);
}
/**
* @return BelongsTo<ContactPhoneNumber, $this>
*/
public function phoneNumber(): BelongsTo
{
return $this->belongsTo(ContactPhoneNumber::class, 'contact_phone_number_id');
}
protected function casts(): array
{
return [
'provider_response' => 'array',
'sent_at' => 'datetime',
];
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Model;
#[Fillable([
'source',
'status',
'started_at',
'finished_at',
'fetched_contacts',
'synced_contacts',
'deleted_contacts',
'message',
'error',
])]
class SourceSyncRun extends Model
{
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'started_at' => 'datetime',
'finished_at' => 'datetime',
];
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Database\Factories\UserFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
class User extends Authenticatable
{
/** @use HasFactory<UserFactory> */
use HasFactory, Notifiable;
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}