<?php
namespace App\Entity;
use App\Entity\Interfaces\SpotHitCampaignInterface;
use App\Traits\DateTrait;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity
*
* @ORM\Table(uniqueConstraints={
* @ORM\UniqueConstraint(columns={"nom"})
* })
* @UniqueEntity(fields={"nom"})
*/
class SmsSpotHitCampaign implements SpotHitCampaignInterface
{
use DateTrait;
const DESTINATAIRE_TYPE_DATAS = 'datas';
const DESTINATAIRE_TYPE_ALL = 'all';
const DESTINATAIRE_TYPE_GROUPE = 'groupe';
/**
* @var int|null
*
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected ?int $id = null;
/**
* @var integer|null
*
* @ORM\Column(type="integer", nullable=true)
*
* @Assert\GreaterThanOrEqual(value = 1)
* @Assert\Type(type="integer")
*/
protected ?int $idSpotHit = null;
/**
* @var string
*
* @ORM\Column(type="string")
*
* @Assert\NotBlank(groups={"new", "Default"})
* @Assert\Type(type="string", groups={"new", "Default"})
*
*/
protected string $message = '';
/**
* @var array|string[]
*
* @ORM\Column(type="json")
*
* @Assert\NotBlank
*/
protected array $destinataires = [];
/**
* @var array|string[]
*
* @ORM\Column(type="json")
*/
protected array $stats = [];
/**
* @var string|null
*
* Si vide, l'expéditeur du SMS sera un numéro court à 5 chiffres auxquels les destinataires peuvent répondre
* L’expéditeur doit comporter un minimum de 3 caractères pour être personnalisé et ne doit pas commencer par plus de 3 chiffres consécutifs avant la première lettre.
*
* @ORM\Column(type="string", length=11, nullable=true)
*
* @Assert\Length(min=3, max=11)
* @Assert\Regex("/^(?![0-9]{3})\w+$/")
* @Assert\Type(type="string")
*/
protected ?string $expediteur = null;
/**
* @var \DateTime|null
*
* @ORM\Column(type="datetime", nullable=true)
*
* @Assert\Type(type="\DateTime")
*/
protected ?\DateTime $date = null;
/**
* @var boolean
*
* Si true, autorise l'envoi de SMS supérieur à 160 caractères
*
* @ORM\Column(type="boolean")
* @Assert\Type(type="bool")
*/
protected bool $smslong = true;
/**
* @var integer|null
*
* Permet de vérifier la taille du SMS long envoyé. Doit correspondre au nombre de caractères su sms, Si le compteur spothit indique un nombre différent, le message sera rejeté.
*
* @ORM\Column(type="integer", nullable=true)
*
* @Assert\GreaterThanOrEqual(value = 1)
* @Assert\Type(type="integer")
*/
protected ?int $smslongnbr = null;
/**
* @var boolean
*
* Si true, tronque automatiquement le message à 160 caractères.
*
* @ORM\Column(type="boolean")
* @Assert\Type(type="bool")
*/
protected bool $tronque = false;
/**
* @var string|null
*
* "auto" => UTF8, "ucs2" => unicode
*
* @ORM\Column(type="string", nullable=true)
*
* @Assert\Choice({"auto", "ucs2", null})
* @Assert\Type(type="string")
*/
protected ?string $encodage = null;
/**
* @var string|null
*
* nom de la campagne
*
* @ORM\Column(type="string", nullable=true)
*
* @Assert\Length(min=1, max=50, groups={"new", "Default"})
* @Assert\Type(type="string", groups={"new", "Default"})
*/
protected ?string $nom = null;
/**
* @var string|null
*
* "all" => sélection de tous les contacts du compte
* "groupe" => sélection de tous les contacts des groupes fournis dans le champ « destinataires »
* "datas" => permet d'ajouter des données personnalisées aux « destinataires » pour les utiliser dans votre message (exemple : "Bonjour {nom} {prenom}")
* il faut que le champ « destinataires » soit un tableau de cette forme : ["+33600000001" => ["nom" => "Nom 1", "prenom" => "Prénom 1"], "+33600000002" => ["nom" => "Nom 2", "prenom" => "Prénom 2"] ...]
*
* @ORM\Column(type="string", nullable=true)
*
* @Assert\Choice({"all", "groupe", "datas", null})
* @Assert\Type(type="string")
*/
protected ?string $destinatairesType = null;
/**
* @var string|null
*
* Adresse URL de votre serveur pour la réception en "push" des statuts après l'envoi.
* Vous devez déjà avoir une adresse paramétrée sur votre compte pour activer les retours "push".
* Si ce paramètre est renseigné, cette URL sera appelée pour cet envoi sinon l'adresse du compte est utilisée.
*
* @ORM\Column(type="string", nullable=true)
*
* @Assert\Type(type="string")
*/
protected ?string $url = null;
/**
* @var \DateTime|null
*
* Obligatoire pour l'envoi échelonné
*
* @ORM\Column(type="datetime", nullable=true)
*
* @Assert\Type(type="\DateTime")
*/
protected ?\DateTime $dateDebut = null;
/**
* @var \DateTime|null
*
* Obligatoire pour l'envoi échelonné
*
* @ORM\Column(type="datetime", nullable=true)
*
* @Assert\Type(type="\DateTime")
*/
protected ?\DateTime $dateFin = null;
/**
* @var array|int[]|null
*
* Obligatoire pour l'envoi échelonné
* Heure(s) d'envois (du lundi au samedi de 8h00 à 22h00 hors jours fériés)
* Tableau avec 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
* La campagne sera fractionnée proportionnellement aux nombres de créneaux entre le jour et l'heure de démarrage, et le jour et l'heure de fin souhaitée.
*
* @ORM\Column(type="json", nullable=true)
*
* @Assert\All({
* @Assert\Type(type="int"),
* })
*
* @Assert\NotBlank(allowNull=true)
*/
protected ?array $creneaux = null;
/**
* @var integer|null
*
* Obligatoire pour l'envoi échelonné
* 1,2,3,4 ou 6
* Nombre d'envoi(s) par heure
*
* @ORM\Column(type="integer", nullable=true)
*
* @Assert\Choice({1, 2, 3, 4, 6})
* @Assert\Type(type="integer")
*/
protected ?int $creneauxHeure = null;
/**
* @var integer|null
*
* Obligatoire pour l'envoi échelonné
* Tableau avec 1,2,3,4,5,6
* Jours d'envoi (1 représentant lundi). Pas d'envoi le dimanche.
*
* @ORM\Column(type="integer", nullable=true)
*
* @Assert\Length(min=1, max=6)
* @Assert\Type(type="integer")
*/
protected ?int $jours = null;
/**
* @var string|null
*
* Fuseau horaire
*
* @ORM\Column(type="string", nullable=true)
*
* @Assert\Type(type="string")
*/
protected ?string $timezone = null;
/**
* @var ContactList|null
*
* @ORM\ManyToOne(targetEntity="ContactList")
* @ORM\JoinColumn(nullable=false)
*
* @Assert\Valid(groups={"new", "Default"})
* @Assert\NotBlank(groups={"new", "Default"})
* @Assert\Type(type="App\Entity\ContactList", groups={"new", "Default"})
*/
protected ?ContactList $contactList = null;
/**
* @var string
*
* @ORM\Column(type="string", nullable=false)
*
* @Assert\Choice({"pending", "processing", "sent", "error", "break"})
* @Assert\Type(type="string")
*/
protected string $statut = self::STATUT_EN_ATTENTE;
/**
* @var string|null
*
* Si statut "en_erreur", il s'agit du message en question
*
* @ORM\Column(type="string", nullable=true)
*
* @Assert\Type(type="string")
*/
protected ?string $erreur = null;
/**
* @return int|null
*/
public function getId(): ?int
{
return $this->id;
}
/**
* @return string
*/
public function getMessage(): string
{
return $this->message;
}
/**
* @param string $message
* @param bool $addStop
*
* @return $this
*/
public function setMessage(string $message, bool $addStop = true): SmsSpotHitCampaign
{
$this->message = trim($message);
if($addStop) $this->addStop();
return $this;
}
/**
* @return $this
*/
public function addStop(): SmsSpotHitCampaign
{
if(strpos($this->message, 'STOP au 36200') === false) $this->message .= ' \n STOP au 36200';
return $this;
}
/**
* @return $this
*/
public function removeStop(): SmsSpotHitCampaign
{
$this->message = trim(str_replace('\n STOP au 36200', '', $this->message));
return $this;
}
/**
* @param bool $useType
*
* @return array|string[]|string
*/
public function getDestinataires(bool $useType = false)
{
$type = $this->getDestinatairesType();
if(!$useType || $type === self::DESTINATAIRE_TYPE_DATAS) return $this->destinataires;
if(!$type || $type === self::DESTINATAIRE_TYPE_GROUPE) return implode(",", $this->destinataires);
return [];
}
/**
* @return int
*/
public function getNbDestinataires(): int
{
return count($this->getDestinataires());
}
/**
* @return int
*/
public function getNbUsersList(): int
{
return $this->contactList ? $this->contactList->getUsers()->count() : 0;
}
/**
* @param array $destinataires
*
* @return $this
*/
public function setDestinataires(array $destinataires, bool $throwException = true): SmsSpotHitCampaign
{
if($this->destinatairesType === self::DESTINATAIRE_TYPE_ALL)
{
if(!empty($destinataires) && $throwException) throw new \InvalidArgumentException("Si le type est '".self::DESTINATAIRE_TYPE_ALL."', il n'y a pas de destinataires à renseigner");
$this->destinataires = [];
}
else
{
foreach($destinataires as $key => $val)
{
if($this->destinatairesType === self::DESTINATAIRE_TYPE_DATAS)
{
$numero = $key;
$datas = $val;
if(!is_array($val))
{
if($throwException) throw new \InvalidArgumentException("Si le type est '".self::DESTINATAIRE_TYPE_DATAS."', le tableau doit est multidimensionnel avec en clef le numero et en valeurs un tableau de datas");
$numero = $val;
$datas = [];
}
}
// groupe ou null
else
{
$numero = $val;
$datas = null;
if(is_array($val))
{
if($throwException) throw new \InvalidArgumentException("Si le type n'est pas '".self::DESTINATAIRE_TYPE_DATAS."', le tableau doit seulement contenir des numeros ou des groupes");
$numero = $key;
}
}
$this->addDestinataire($numero, $datas, $throwException);
}
}
return $this;
}
/**
* @param string $numero ou groupe
* @param array|null $datas à utiliser seulement si le type de destinataire est "datas"
* @param bool $throwException
*
* @return $this
*/
public function addDestinataire(string $numero, ?array $datas = null, bool $throwException = true): SmsSpotHitCampaign
{
$baseNumero = $numero;
switch($this->destinatairesType)
{
case self::DESTINATAIRE_TYPE_DATAS:
if($datas === null)
{
if($throwException) throw new \InvalidArgumentException("Si le type est '".self::DESTINATAIRE_TYPE_DATAS."', datas doit être un tableau de variables ou vide");
$datas = [];
}
break;
case self::DESTINATAIRE_TYPE_ALL:
if($throwException) throw new \InvalidArgumentException("Si le type est '".self::DESTINATAIRE_TYPE_ALL."', il est impossible de rajouter des destinataires");
return $this;
default:
if($datas !== null)
{
if($throwException) throw new \InvalidArgumentException("Si le type n'est pas '".self::DESTINATAIRE_TYPE_DATAS."', datas doit être null");
$datas = null;
}
}
if($this->destinatairesType !== self::DESTINATAIRE_TYPE_GROUPE)
{
if(!$numero = $this->checkMobile($numero))
{
if($throwException) throw new \InvalidArgumentException("Le numero '$baseNumero' est invalide");
return $this;
}
}
if(!$datas) {
if(!in_array($numero, $this->destinataires)) $this->destinataires[] = $numero;
}
else {
if(!isset($this->destinataires[$numero])) $this->destinataires[$numero] = $datas;
}
return $this;
}
/**
* @param string|null $numero
*
* @return string|false
*/
static function checkMobile(?string $numero)
{
if(!$numero) return false;
$numero = trim($numero);
$numero = preg_replace('/[^0-9+]/', '', $numero);
if(!preg_match('/\+[1-9]\d{10,14}/', $numero))
{
$numero = str_replace('+', '', $numero);
while(substr($numero, 0, 1) === '0')
{
$numero = substr($numero, 1, strlen($numero) -1);
}
$numero = '+33'.$numero;
}
if(!preg_match('/\+[1-9]\d{10,14}/', $numero)) return false;
//validation mobile FR
if(
substr($numero, 0, 3) === '+33'
&& (
strlen($numero) !== 12
|| (substr($numero, 0, 4) !== '+336' && substr($numero, 0, 4) !== '+337')
)
){
return false;
}
return $numero;
}
/**
* @param string $numero
*
* @return $this
*/
public function removeDestinataire(string $numero): SmsSpotHitCampaign
{
$numero = $this->checkMobile($numero);
if(!$numero) return $this;
if($this->getDestinatairesType() === self::DESTINATAIRE_TYPE_DATAS)
{
unset($this->destinataires[$numero]);
}
else
{
$key = array_search($numero, $this->destinataires);
unset($this->destinataires[$key]);
}
return $this;
}
/**
* @return string|null
*/
public function getExpediteur(): ?string
{
return $this->expediteur;
}
/**
* @param string|null $expediteur
*
* @return $this
*/
public function setExpediteur(?string $expediteur): SmsSpotHitCampaign
{
$this->expediteur = $expediteur;
return $this;
}
/**
* @return bool
*/
public function isSmslong(): bool
{
return $this->smslong;
}
/**
* @param bool $smslong
*
* @return $this
*/
public function setSmslong(bool $smslong): SmsSpotHitCampaign
{
$this->smslong = $smslong;
return $this;
}
/**
* @return int|null
*/
public function getSmslongnbr(): ?int
{
return $this->smslongnbr;
}
/**
* @param int|null $smslongnbr
*
* @return $this
*/
public function setSmslongnbr(?int $smslongnbr): SmsSpotHitCampaign
{
$this->smslongnbr = $smslongnbr;
return $this;
}
/**
* @return bool
*/
public function isTronque(): bool
{
return $this->tronque;
}
/**
* @param bool $tronque
*
* @return $this
*/
public function setTronque(bool $tronque): SmsSpotHitCampaign
{
$this->tronque = $tronque;
return $this;
}
/**
* @return string|null
*/
public function getEncodage(): ?string
{
return $this->encodage;
}
/**
* @param string|null $encodage
*
* @return $this
*/
public function setEncodage(?string $encodage): SmsSpotHitCampaign
{
$this->encodage = $encodage;
return $this;
}
/**
* @return string|null
*/
public function getNom(): ?string
{
return $this->nom;
}
/**
* @param string|null $nom
*
* @return $this
*/
public function setNom(?string $nom): SmsSpotHitCampaign
{
$this->nom = $nom;
return $this;
}
/**
* @return string|null
*/
public function getDestinatairesType(): ?string
{
return $this->destinatairesType;
}
/**
* Le contenu de "destinataires" actuel doit être adapté en fonction du type, en principe il faut définir le type avant "destinataires"
*
* @param string|null $destinatairesType
*
* @return $this
*/
public function setDestinatairesType(?string $destinatairesType): SmsSpotHitCampaign
{
if($destinatairesType !== $this->destinatairesType && !empty($this->destinataires))
{
$destinataires = [];
if($destinatairesType === self::DESTINATAIRE_TYPE_ALL) $this->destinataires = [];
if(($destinatairesType === self::DESTINATAIRE_TYPE_GROUPE || !$destinatairesType) && $this->destinatairesType === self::DESTINATAIRE_TYPE_DATAS)
{
foreach($this->destinataires as $numOrGroup => $datas)
{
$destinataires[] = $numOrGroup;
}
$this->destinataires = $destinataires;
}
if($destinatairesType === self::DESTINATAIRE_TYPE_DATAS)
{
foreach($this->destinataires as $numOrGroup)
{
$destinataires[$numOrGroup] = [];
}
$this->destinataires = $destinataires;
}
}
$this->destinatairesType = $destinatairesType;
return $this;
}
/**
* @return string|null
*/
public function getUrl(): ?string
{
return $this->url;
}
/**
* @param string|null $url
*
* @return $this
*/
public function setUrl(?string $url): SmsSpotHitCampaign
{
$this->url = $url;
return $this;
}
/**
* @return array|int[]|null
*/
public function getCreneaux(): ?array
{
return $this->creneaux;
}
/**
* @param array|null $creneaux
*
* @return $this
*/
public function setCreneaux(?array $creneaux): SmsSpotHitCampaign
{
$this->creneaux = $creneaux;
return $this;
}
/**
* @return int|null
*/
public function getCreneauxHeure(): ?int
{
return $this->creneauxHeure;
}
/**
* @param int|null $creneauxHeure
*
* @return $this
*/
public function setCreneauxHeure(?int $creneauxHeure): SmsSpotHitCampaign
{
$this->creneauxHeure = $creneauxHeure;
return $this;
}
/**
* @return int|null
*/
public function getJours(): ?int
{
return $this->jours;
}
/**
* @param int|null $jours
*
* @return $this
*/
public function setJours(?int $jours): SmsSpotHitCampaign
{
$this->jours = $jours;
return $this;
}
/**
* @return string|null
*/
public function getTimezone(): ?string
{
return $this->timezone;
}
/**
* @param string|null $timezone
*
* @return $this
*/
public function setTimezone(?string $timezone): SmsSpotHitCampaign
{
$this->timezone = $timezone;
return $this;
}
/**
* @return \DateTime|null
*/
public function getDate(): ?\DateTime
{
return $this->date;
}
/**
* @param \DateTime|null $date
*
* @return $this
*/
public function setDate(?\DateTime $date): SmsSpotHitCampaign
{
$this->date = $date;
return $this;
}
/**
* @return \DateTime|null
*/
public function getDateDebut(): ?\DateTime
{
return $this->dateDebut;
}
/**
* @param \DateTime|null $dateDebut
*
* @return $this
*/
public function setDateDebut(?\DateTime $dateDebut): SmsSpotHitCampaign
{
$this->dateDebut = $dateDebut;
return $this;
}
/**
* @return \DateTime|null
*/
public function getDateFin(): ?\DateTime
{
return $this->dateFin;
}
/**
* @param \DateTime|null $dateFin
*
* @return $this
*/
public function setDateFin(?\DateTime $dateFin): SmsSpotHitCampaign
{
$this->dateFin = $dateFin;
return $this;
}
/**
* @return int|null
*/
public function getIdSpotHit(): ?int
{
return $this->idSpotHit;
}
/**
* @param int|null $idSpotHit
*
* @return $this
*/
public function setIdSpotHit(?int $idSpotHit): SmsSpotHitCampaign
{
$this->idSpotHit = $idSpotHit;
return $this;
}
/**
* @return string
*/
public function getProduit(): string
{
return SpotHitCampaignInterface::PRODUIT_SMS;
}
/**
* @return ContactList|null
*/
public function getContactList(): ?ContactList
{
return $this->contactList;
}
/**
* @param ContactList|null $contactList
*
* @return $this
*/
public function setContactList(?ContactList $contactList): SmsSpotHitCampaign
{
$this->contactList = $contactList;
return $this;
}
/**
* @return string
*/
public function getStatut(): string
{
return $this->statut;
}
/**
* @param string $statut
*
* @return $this
*/
public function setStatut(string $statut): SmsSpotHitCampaign
{
$this->statut = $statut;
return $this;
}
/**
* @return string|null
*/
public function getErreur(): ?string
{
return $this->erreur;
}
/**
* @param string|null $erreur
*
* @return $this
*/
public function setErreur(?string $erreur): SmsSpotHitCampaign
{
$this->erreur = $erreur;
return $this;
}
/**
* @return array|string[]|string
*/
public function getStats(bool $onlyDatas = false, bool $onlyLabels = false, bool $jsonEncode = false)
{
$array = $this->stats;
if($onlyDatas)
{
$array = [];
foreach($this->stats as $label => $data)
{
$array[] = $data;
}
}
if($onlyLabels)
{
$array = [];
foreach($this->stats as $label => $data)
{
$array[] = $label;
}
}
return $jsonEncode ? json_encode($array) : $array;
}
/**
* @param array $stats
*
* @return $this
*/
public function setStats(array $stats): SmsSpotHitCampaign
{
$this->stats = $stats;
return $this;
}
/**
* @return bool
*/
public function isEditable(): bool
{
return $this->statut === self::STATUT_EN_ATTENTE || $this->statut === self::STATUT_EN_PAUSE;
}
/**
* @return bool
*/
public function isEnvoyee(): bool
{
return $this->statut !== self::STATUT_EN_ERREUR && !$this->isEditable();
}
/**
* @return bool
*/
public function isTerminee(): bool
{
return $this->statut === self::STATUT_TERMINEE;
}
/**
* @return bool
*/
public function isErreur(): bool
{
return $this->statut === self::STATUT_EN_ERREUR;
}
}