aboutsummaryrefslogtreecommitdiff
path: root/src/Model/Entity
diff options
context:
space:
mode:
Diffstat (limited to 'src/Model/Entity')
-rw-r--r--src/Model/Entity/AppMetadata.php46
-rw-r--r--src/Model/Entity/Article.php94
-rw-r--r--src/Model/Entity/Asset.php68
-rw-r--r--src/Model/Entity/AssetEmployment.php54
-rw-r--r--src/Model/Entity/Email.php131
-rw-r--r--src/Model/Entity/EmailForm.php57
-rw-r--r--src/Model/Entity/Event.php129
-rw-r--r--src/Model/Entity/Image.php89
-rw-r--r--src/Model/Entity/Log.php38
-rw-r--r--src/Model/Entity/Node.php167
-rw-r--r--src/Model/Entity/NodeData.php102
-rw-r--r--src/Model/Entity/Page.php222
-rw-r--r--src/Model/Entity/Presentation.php135
-rw-r--r--src/Model/Entity/User.php67
14 files changed, 1399 insertions, 0 deletions
diff --git a/src/Model/Entity/AppMetadata.php b/src/Model/Entity/AppMetadata.php
new file mode 100644
index 0000000..b7d6923
--- /dev/null
+++ b/src/Model/Entity/AppMetadata.php
@@ -0,0 +1,46 @@
1<?php
2// src/Model/Entity/AppMetadata.php
3
4// comme dans AppMode, prévoir d'ajouter des champs "since" et "by" (qui a changé quoi quel jour?)
5
6declare(strict_types=1);
7
8namespace App\Entity;
9
10use Doctrine\ORM\Mapping as ORM;
11
12#[ORM\Entity]
13#[ORM\Table(name: TABLE_PREFIX . "app_metadata")]
14class AppMetadata
15{
16 #[ORM\Id]
17 #[ORM\Column(type: "string", length: 100)]
18 private string $key_name;
19
20 #[ORM\Column(type: "string")]
21 private string $value;
22
23 public function __construct(string $key, string $value)
24 {
25 $this->key_name = $key;
26 $this->value = $value;
27 }
28
29 public function getKey(): string
30 {
31 return $this->key_name;
32 }
33 public function getValue(): string
34 {
35 return $this->value;
36 }
37
38 public function setKey(string $key): void
39 {
40 $this->key_name = $key;
41 }
42 public function setValue(string $value): void
43 {
44 $this->value = $value;
45 }
46} \ No newline at end of file
diff --git a/src/Model/Entity/Article.php b/src/Model/Entity/Article.php
new file mode 100644
index 0000000..588ebd6
--- /dev/null
+++ b/src/Model/Entity/Article.php
@@ -0,0 +1,94 @@
1<?php
2// src/Model/Entity/Article.php
3//
4// entité commune pour les "post" (articles simples) et les "news" (complexes avec titre, aperçu et contenu)
5
6declare(strict_types=1);
7
8namespace App\Entity;
9
10use Doctrine\ORM\Mapping as ORM;
11use Doctrine\Common\Collections\ArrayCollection;
12use Doctrine\Common\Collections\Collection;
13
14#[ORM\Entity]
15#[ORM\Table(name: TABLE_PREFIX . "article")]
16class Article
17{
18 #[ORM\Id]
19 #[ORM\GeneratedValue]
20 #[ORM\Column(type: "integer")]
21 private int $id_article;
22
23 // datetime_immutable permet à la base de toujours gérer cette clé primaire correctement
24 #[ORM\Column(type: 'datetime', options: ['default' => 'CURRENT_TIMESTAMP'], unique: true)]
25 private ?\DateTime $date_time; // le type datetime de doctrine convertit en type \DateTime de PHP
26
27 #[ORM\Column(type: "text")] // "string" => varchar(255) => risque HTML non valide
28 private string $title;
29
30 #[ORM\Column(type: "text")]
31 private string $preview; // une simple textarea
32
33 #[ORM\Column(type: "text")]
34 private string $content; // de l'éditeur html
35
36 #[ORM\ManyToMany(targetEntity: Image::class, inversedBy: "article")] // cascade: ['remove'] = très dangereux!
37 #[ORM\JoinTable(
38 name: TABLE_PREFIX . "article_image",
39 joinColumns: [new ORM\JoinColumn(name: "article_id", referencedColumnName: "id_article", onDelete: "CASCADE")], // onDelete: "CASCADE": très utile
40 inverseJoinColumns: [new ORM\JoinColumn(name: "image_id", referencedColumnName: "id_image", onDelete: "CASCADE")]
41 )]
42 private Collection $images;
43
44 public function __construct(string $content, ?\DateTime $date_time = null, string $title = '', string $preview = '')
45 {
46 $this->date_time = $date_time;
47 $this->title = $title;
48 $this->preview = $preview;
49 $this->content = $content;
50 $this->images = new ArrayCollection(); // initialisation nécessaire
51 }
52
53 public function getDateTime(): \DateTime
54 {
55 return $this->date_time;
56 }
57 public function setDateTime(\DateTime $date_time): void
58 {
59 $this->date_time = $date_time;
60 }
61 public function getTimestamp(): int
62 {
63 return $this->date_time->getTimestamp();
64 }
65 public function getTitle(): string
66 {
67 return $this->title;
68 }
69 public function setTitle(string $title): void
70 {
71 $this->title = $title;
72 }
73 public function getPreview(): string
74 {
75 return $this->preview;
76 }
77 public function setPreview(string $preview): void
78 {
79 $this->preview = $preview;
80 }
81 public function getContent(): string
82 {
83 return $this->content;
84 }
85 public function setContent(string $data): void
86 {
87 $this->content = $data;
88 }
89
90 public function getImages(): Collection
91 {
92 return $this->images;
93 }
94}
diff --git a/src/Model/Entity/Asset.php b/src/Model/Entity/Asset.php
new file mode 100644
index 0000000..51e21fe
--- /dev/null
+++ b/src/Model/Entity/Asset.php
@@ -0,0 +1,68 @@
1<?php
2// src/Model/Entity/Asset.php
3
4declare(strict_types=1);
5
6namespace App\Entity;
7
8use Doctrine\ORM\Mapping as ORM;
9use Doctrine\Common\Collections\Collection;
10
11#[ORM\Entity]
12#[ORM\Table(name: TABLE_PREFIX . "asset")]
13class Asset
14{
15 const PATH = 'assets/';
16 const USER_PATH = 'user_data/assets/';
17 // choisir un répertoire du genre /var/www/html/uploads/? ou au moins hors de /src? j'en sais rien
18
19 #[ORM\Id]
20 #[ORM\GeneratedValue]
21 #[ORM\Column(type: "integer")]
22 private int $id_asset;
23
24 #[ORM\Column(type: "string", length: 255)] // nom de fichier modifié avec uniqid (fichiers différents de même nom)
25 private string $file_name;
26
27 #[ORM\Column(type: "string", length: 255, nullable: true)]
28 private string $mime_type; // image/jpeg, image/png, etc
29
30 #[ORM\Column(type: "string", length: 64, unique: true)] // doctrine n'a pas d'équivalent au type CHAR des BDD (on voulait CHAR(64)), c'est pas grave!
31 private string $hash; // pour détecter deux fichiers identiques, même si leurs noms et les métadonnées changent
32
33 #[ORM\OneToMany(mappedBy: 'asset', targetEntity: AssetEmployment::class)]
34 private Collection $asset_employment;
35
36 public function __construct(string $name, string $mime_type, string $hash)
37 {
38 $this->file_name = $name;
39 $this->mime_type = $mime_type;
40 $this->hash = $hash;
41 }
42
43 public function getFileName(): string
44 {
45 return $this->file_name;
46 }
47 public function setFileName(string $name): void
48 {
49 $this->file_name = $name;
50 }
51 public function getMimeType(): string
52 {
53 return $this->mime_type;
54 }
55 public function setMimeType(string $mime_type): void
56 {
57 $this->mime_type = $mime_type;
58 }
59 public function getHash(): string
60 {
61 return $this->hash;
62 }
63
64 public function getAssetEmployments(): Collection
65 {
66 return $this->asset_employment;
67 }
68}
diff --git a/src/Model/Entity/AssetEmployment.php b/src/Model/Entity/AssetEmployment.php
new file mode 100644
index 0000000..e608af0
--- /dev/null
+++ b/src/Model/Entity/AssetEmployment.php
@@ -0,0 +1,54 @@
1<?php
2// src/Model/Entity/NodeDataAsset.php
3//
4// entité intermédiaire 3 colonnes conçue selon le principe "slot <=> ressource unique" (paires node_data/role uniques)
5// doctrine gère mal les clés primaires triples, j'ai donc choisi une clé primaire double node_data_id/role
6
7declare(strict_types=1);
8
9namespace App\Entity;
10
11use Doctrine\ORM\Mapping as ORM;
12
13#[ORM\Entity]
14#[ORM\Table(name: TABLE_PREFIX . 'asset_employment')]
15class AssetEmployment
16{
17 // clé primaire double
18 #[ORM\Id]
19 #[ORM\ManyToOne(targetEntity: NodeData::class, inversedBy: 'asset_employment')]
20 #[ORM\JoinColumn(name: 'node_data_id', referencedColumnName: 'id_node_data', onDelete: 'CASCADE')]
21 private NodeData $node_data;
22
23 #[ORM\Id]
24 #[ORM\Column(type: 'string', length: 50)]
25 private string $role;
26
27 #[ORM\ManyToOne(targetEntity: Asset::class, inversedBy: 'asset_employment')]
28 #[ORM\JoinColumn(name: 'asset_id', referencedColumnName: 'id_asset', onDelete: 'CASCADE')]
29 private Asset $asset;
30
31 public function __construct(NodeData $node_data, Asset $asset, string $role){
32 $this->node_data = $node_data;
33 $this->asset = $asset;
34 $this->role = $role;
35 }
36
37 /*public function getNodeData(): NodeData
38 {
39 return $this->node_data;
40 }*/
41 public function getAsset(): Asset
42 {
43 return $this->asset;
44 }
45 public function setAsset(Asset $asset): self
46 {
47 $this->asset = $asset;
48 return $this;
49 }
50 public function getRole(): string
51 {
52 return $this->role;
53 }
54}
diff --git a/src/Model/Entity/Email.php b/src/Model/Entity/Email.php
new file mode 100644
index 0000000..8ad66fd
--- /dev/null
+++ b/src/Model/Entity/Email.php
@@ -0,0 +1,131 @@
1<?php
2// src/Model/Entity/Email.php
3
4declare(strict_types=1);
5
6namespace App\Entity;
7
8use Doctrine\ORM\Mapping as ORM;
9
10#[ORM\Entity]
11#[ORM\Table(name: TABLE_PREFIX . "email")]
12class Email
13{
14 // en mois
15 const DEFAULT_RETENTION_PERIOD = 36; // 3 ans, justification = prospection, durée "glissante", date de suppression remise à jour à chaque nouvel e-mail
16 const DEFAULT_RETENTION_PERIOD_SENSITIVE = 60; // 5 ans pour données sensibles ou litige, durée de preuve légale, durée non glissante
17
18 #[ORM\Id]
19 #[ORM\GeneratedValue]
20 #[ORM\Column(type: "integer")]
21 private int $id_email;
22
23 #[ORM\Column(type: "string", length: 255)]
24 private string $sender_name;
25
26 #[ORM\Column(type: "string", length: 320)]
27 private string $sender_address;
28
29 #[ORM\Column(type: "string", length: 320)]
30 private string $recipient;
31
32 // inutile, objet = 'Message envoyé par ' . $name . ' (' . $email . ') depuis le site web'
33 /*#[ORM\Column(type: "text")]
34 private string $subject;*/
35
36 #[ORM\Column(type: "text")]
37 private string $content;
38
39 #[ORM\Column(type: 'datetime', options: ['default' => 'CURRENT_TIMESTAMP'])]
40 private \DateTime $date_time;
41
42 #[ORM\Column(type: 'boolean')]
43 private bool $is_sensitive; // "sensitive" tout court est un mot réservé
44
45 #[ORM\Column(type: 'datetime', options: ['default' => 'CURRENT_TIMESTAMP'])]
46 private \DateTime $last_contact_date;
47
48 #[ORM\Column(type: 'datetime', nullable: true)]
49 private ?\DateTime $is_sensitive_since;
50
51 #[ORM\ManyToOne(targetEntity: EmailForm::class)]
52 #[ORM\JoinColumn(name: "email_form_id", referencedColumnName: "id_email_form", nullable: true)]
53 private ?EmailForm $email_form;
54
55 public function __construct(string $sender_name, string $sender_address, string $recipient, string $content, EmailForm $email_form, bool $sensitive = false){
56 $this->sender_name = strtolower($sender_name);
57 $this->sender_address = strtolower($sender_address);
58 $this->recipient = strtolower($recipient);
59 $this->content = $content;
60 $this->date_time = new \DateTime;
61 $this->last_contact_date = new \DateTime;
62 $this->email_form = $email_form;
63 $this->makeSensitive($sensitive);
64 }
65
66 public function getId(): int
67 {
68 return $this->id_email;
69 }
70 public function getSenderName(): string
71 {
72 return $this->sender_name;
73 }
74 public function getSenderAddress(): string
75 {
76 return $this->sender_address;
77 }
78 public function getRecipient(): string
79 {
80 return $this->recipient;
81 }
82 public function getContent(): string
83 {
84 return $this->content;
85 }
86 public function getDateTime(): \DateTime
87 {
88 return $this->date_time;
89 }
90 /*public function getLastContactDate(): \DateTime
91 {
92 return $this->last_contact_date;
93 }*/
94 public function isSensitive(): bool
95 {
96 return $this->is_sensitive;
97 }
98 public function isSensitiveSince(): ?\DateTime
99 {
100 return $this->is_sensitive_since;
101 }
102
103 public function makeSensitive(bool $sensitive = true): void
104 {
105 $this->is_sensitive = $sensitive;
106 if($sensitive && $this->is_sensitive_since === null){
107 $this->is_sensitive_since = new \DateTime();
108 }
109 }
110
111 public function updateLastContactDate(): void
112 {
113 $this->last_contact_date = new \DateTime;
114 }
115
116 public function getDeletionDate(): \DateTime // utilise une durée de conservation $period qui est propre au bloc formulaire (à son EmailForm)
117 {
118 // tests appliqués:
119 // => e-mail associé à un formulaire?
120 // => ce formulaire dispose d'une durée de stockage spécifique?
121 // => cette donnée est un entier > 0
122 $key = $this->is_sensitive ? 'retention_period_sensible' : 'retention_period';
123 $period = $this->email_form ? (int)($this->email_form->getData()[$key] ?? null) : null;
124
125 $default = $this->is_sensitive ? self::DEFAULT_RETENTION_PERIOD_SENSITIVE : self::DEFAULT_RETENTION_PERIOD;
126 $period = ($period === null || $period <= 0) ? $default : $period;
127
128 $date = clone ($this->is_sensitive ? $this->is_sensitive_since : $this->last_contact_date); // erreur si "sensible" mais sans date disponible (pas censé arriver)
129 return $date->modify('+ ' . (string)$period . ' month');
130 }
131} \ No newline at end of file
diff --git a/src/Model/Entity/EmailForm.php b/src/Model/Entity/EmailForm.php
new file mode 100644
index 0000000..5c54b86
--- /dev/null
+++ b/src/Model/Entity/EmailForm.php
@@ -0,0 +1,57 @@
1<?php
2// src/Model/Entity/EmailForm.php
3
4declare(strict_types=1);
5
6namespace App\Entity;
7
8use Doctrine\ORM\Mapping as ORM;
9
10#[ORM\Entity]
11#[ORM\Table(name: TABLE_PREFIX . "email_form")]
12class EmailForm{
13 #[ORM\Id]
14 #[ORM\GeneratedValue]
15 #[ORM\Column(type: "integer")]
16 private int $id_email_form;
17
18 // inverseBy fait le lien avec $email_form dans Node (qui a "mappedBy")
19 #[ORM\OneToOne(targetEntity: Node::class, inversedBy: "email_form")]
20 #[ORM\JoinColumn(name: "node_id", referencedColumnName: "id_node")]
21 private ?Node $node;
22
23 #[ORM\Column(type: "json")]
24 private array $data;
25
26 public function __construct(array $data, Node $node){
27 $this->data = $data;
28 $this->node = $node;
29 }
30
31 public function getId(): int
32 {
33 return $this->id_email_form;
34 }
35
36 // getData et updateData sont indentiques au code dans NodeData
37 // plutôt qu'une interface, pourquoi pas une classe abstraite? ou peut-être un trait?
38 public function getData(): array
39 {
40 return $this->data;
41 }
42 public function updateData(string $key, string|int|bool|array $value = ''): void
43 {
44 if($value !== ''){
45 $this->data[$key] = $value;
46 }
47 // si $value est vide, supprime la clé
48 elseif(isset($this->data[$key])){
49 unset($this->data[$key]);
50 }
51 }
52
53 public function setNode(?Node $node): void
54 {
55 $this->node = $node;
56 }
57} \ No newline at end of file
diff --git a/src/Model/Entity/Event.php b/src/Model/Entity/Event.php
new file mode 100644
index 0000000..3b81da7
--- /dev/null
+++ b/src/Model/Entity/Event.php
@@ -0,0 +1,129 @@
1<?php
2// src/Model/Entity/Event.php
3
4declare(strict_types=1);
5
6namespace App\Entity;
7
8use Doctrine\ORM\Mapping as ORM;
9
10#[ORM\Entity]
11#[ORM\Table(name: TABLE_PREFIX . 'event')]
12class Event
13{
14 #[ORM\Id]
15 #[ORM\GeneratedValue]
16 #[ORM\Column(type: 'integer')]
17 private int $id;
18
19 #[ORM\Column(type: 'string', length: 255)] // type varchar(255)
20 private string $title;
21 // contrôle JS: if(title.length > 255)
22
23 #[ORM\Column(type: 'text')]
24 private string $description = '';
25 // chatgpt: Dans un contexte API/Front comme FullCalendar,
26 // préférer une chaîne vide à une varaible "null" peut être plus pratique,
27 // car ça évite des contrôles côté JS.
28
29 #[ORM\Column(type: 'datetime')] // chatgpt: Doctrine suppose UTC si pas de configuration spécifique
30 private \DateTimeInterface $start; // typage possible avec une interface,
31 //chatgpt: choix \DateTime par défaut, autorise \DateTimeImmutable
32
33 #[ORM\Column(type: 'datetime')]
34 private \DateTimeInterface $end;
35
36 #[ORM\Column(type: 'boolean')]
37 private bool $all_day;
38
39 #[ORM\Column(type: 'string', length: 7, nullable: true)]
40 private ?string $color = null;
41
42 public function __construct(array $json){
43 $this->securedUpdateFromJSON($json);
44 }
45
46 public function securedUpdateFromJSON(array $json): void
47 {
48 $this->title = htmlspecialchars($json['title']);
49 $this->description = htmlspecialchars($json['description']);
50 try{
51 $this->start = new \Datetime($json['start']);
52 $this->end = new \Datetime($json['end']);
53 }
54 catch(\Exception $e){
55 throw new \InvalidArgumentException('Bad date input');
56 }
57 $all_day = filter_var($json['allDay'] ?? null, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
58 if(!is_bool($all_day)){
59 throw new \InvalidArgumentException('Bad checkbox input');
60 }
61 $this->all_day = $all_day;
62 $this->color = isset($json['color']) ? htmlspecialchars($json['color']) : null;
63 }
64
65 public function getId(): int
66 {
67 return $this->id;
68 }
69
70 public function getTitle(): string
71 {
72 return $this->title;
73 }
74 /*public function setTitle(string $title): self
75 {
76 $this->title = $title;
77 return $this;
78 }*/
79
80 public function getDescription(): string
81 {
82 return $this->description;
83 }
84 /*public function setDescription(string $description = ''): self
85 {
86 $this->description = $description;
87 return $this;
88 }*/
89
90 public function getStart(): \DateTimeInterface
91 {
92 return $this->start;
93 }
94 /*public function setStart(\DateTimeInterface $start): self
95 {
96 $this->start = $start;
97 return $this;
98 }*/
99
100 public function getEnd(): \DateTimeInterface
101 {
102 return $this->end;
103 }
104 /*public function setEnd(\DateTimeInterface $end): self
105 {
106 $this->end = $end;
107 return $this;
108 }*/
109
110 public function isAllDay(): bool
111 {
112 return $this->all_day;
113 }
114 /*public function setAllDay(bool $all_day): self
115 {
116 $this->all_day = $all_day;
117 return $this;
118 }*/
119
120 public function getColor(): ?string
121 {
122 return $this->color;
123 }
124 /*public function setColor(?string $color): self
125 {
126 $this->color = $color;
127 return $this;
128 }*/
129}
diff --git a/src/Model/Entity/Image.php b/src/Model/Entity/Image.php
new file mode 100644
index 0000000..06230ce
--- /dev/null
+++ b/src/Model/Entity/Image.php
@@ -0,0 +1,89 @@
1<?php
2// src/Model/Entity/Image.php
3
4declare(strict_types=1);
5
6namespace App\Entity;
7
8use Doctrine\Common\Collections\Collection;
9use Doctrine\ORM\Mapping as ORM;
10
11#[ORM\Entity]
12#[ORM\Table(name: TABLE_PREFIX . "image")]
13class Image
14{
15 #[ORM\Id]
16 #[ORM\GeneratedValue]
17 #[ORM\Column(type: "integer")]
18 private int $id_image;
19
20 #[ORM\Column(type: "string", length: 255, unique: true)] // nom d'image UNIQUE
21 private string $file_name;
22
23 // choisir un répertoire du genre /var/www/html/uploads/ de préférence hors de /src
24 #[ORM\Column(type: "string", length: 255, unique: true, nullable: true)]
25 private ?string $file_path;
26
27 #[ORM\Column(type: "string", length: 255, unique: true, nullable: true)]
28 private ?string $file_path_mini;
29
30 #[ORM\Column(type: "string", length: 255, nullable: true)]
31 private string $mime_type; // image/jpeg, image/png, etc
32
33 #[ORM\Column(type: "string", length: 255, nullable: true)]
34 private string $alt; // texte alternatif
35
36 // autre champs optionnels: file_size, date (default current timestamp)
37
38 /* étapes au téléchargement:
39 => Validation du type de fichier : On vérifie que le fichier est bien une image en utilisant le type MIME. On peut aussi vérifier la taille du fichier.
40 => Création d'un répertoire structuré : On génère un chemin dynamique basé sur la date (uploads/2024/12/24/) pour organiser les images.
41 => Génération d'un nom de fichier unique : On utilise uniqid() pour générer un nom unique et éviter les conflits de nom.
42 => Déplacement du fichier sur le serveur : Le fichier est déplacé depuis son emplacement temporaire vers le répertoire uploads/.
43 => Enregistrement dans la base de données : On enregistre les informations de l'image dans la base de données. */
44
45 #[ORM\ManyToMany(targetEntity: Article::class, mappedBy: "images")]
46 private Collection $article;
47
48 public function __construct(string $name, ?string $path, ?string $path_mini, string $mime_type, string $alt)
49 {
50 $this->file_name = $name;
51 $this->file_path = $path;
52 $this->file_path_mini = $path_mini;
53 $this->mime_type = $mime_type;
54 $this->alt = $alt;
55 }
56
57 public function getFileName(): string
58 {
59 return $this->file_name;
60 }
61 public function getFilePath(): string
62 {
63 return $this->file_path;
64 }
65 public function getFilePathMini(): string
66 {
67 return $this->file_path_mini;
68 }
69 public function getAlt(): string
70 {
71 return $this->alt;
72 }
73
74
75 // pour ViewBuilderController?
76 /*public function displayImage($imageId): void
77 {
78 //$imageId = 1; // Exemple d'ID d'image
79 $stmt = $pdo->prepare("SELECT file_path FROM images WHERE id = ?");
80 $stmt->execute([$imageId]);
81 $image = $stmt->fetch();
82
83 if ($image) {
84 echo "<img src='" . $image['file_path'] . "' alt='Image'>";
85 } else {
86 echo "Image non trouvée.";
87 }
88 }*/
89}
diff --git a/src/Model/Entity/Log.php b/src/Model/Entity/Log.php
new file mode 100644
index 0000000..b1cc168
--- /dev/null
+++ b/src/Model/Entity/Log.php
@@ -0,0 +1,38 @@
1<?php
2// src/Model/Entity/Log.php
3
4declare(strict_types=1);
5
6namespace App\Entity;
7
8use Doctrine\ORM\Mapping as ORM;
9
10#[ORM\Entity]
11#[ORM\Table(name: TABLE_PREFIX . "log")]
12class Log
13{
14 #[ORM\Id]
15 #[ORM\GeneratedValue]
16 #[ORM\Column(type: "integer")]
17 private int $id_log;
18
19 #[ORM\Column(type: 'datetime', options: ['default' => 'CURRENT_TIMESTAMP'])] // CURRENT_TIMESTAMP "inutile", date générée par PHP
20 private \DateTime $date_time; // type datetime de doctrine <=> type \DateTime de PHP
21
22 #[ORM\Column(type: "boolean")]
23 private bool $success;
24
25 public function __construct(bool $success){
26 $this->date_time = new \DateTime;
27 $this->success = $success;
28 }
29
30 public function getFormatedDate(): string
31 {
32 return $this->date_time->format('d/m/Y à H\hi');
33 }
34 public function getSuccess(): bool
35 {
36 return $this->success;
37 }
38}
diff --git a/src/Model/Entity/Node.php b/src/Model/Entity/Node.php
new file mode 100644
index 0000000..07666c3
--- /dev/null
+++ b/src/Model/Entity/Node.php
@@ -0,0 +1,167 @@
1<?php
2// src/Model/Entity/Node.php
3
4declare(strict_types=1);
5
6namespace App\Entity;
7
8use Doctrine\ORM\Mapping as ORM;
9use App\Model\Position;
10
11#[ORM\Entity]
12#[ORM\Table(name: TABLE_PREFIX . "node")]
13class Node
14{
15 use Position;
16
17 #[ORM\Id]
18 #[ORM\GeneratedValue]
19 #[ORM\Column(type: "integer")]
20 private ?int $id_node = null;
21
22 #[ORM\Column(type: "string", length: 255)]
23 private string $name_node;
24
25 #[ORM\Column(type: "integer")]
26 private int $position;
27
28 #[ORM\ManyToOne(targetEntity: self::class)]
29 //#[ORM\ManyToOne(targetEntity: self::class, fetch: 'EAGER')] // fetch: 'eager' désactive le mode fainéant de doctrine
30 #[ORM\JoinColumn(name: "parent_id", referencedColumnName: "id_node", onDelete: "SET NULL", nullable: true)]
31 private ?self $parent = null;
32
33 // un onDelete: "CASCADE" serait logique ici mais dangereux, on pourrait aussi faire en sorte que les noeuds soient récupérables si la page est brutalement supprimée
34 #[ORM\ManyToOne(targetEntity: Page::class)]
35 #[ORM\JoinColumn(name: "page_id", referencedColumnName: "id_page", nullable: true)]
36 private ?Page $page;
37
38 #[ORM\ManyToOne(targetEntity: Article::class, cascade: ['persist'])]
39 #[ORM\JoinColumn(name: "article_id", referencedColumnName: "id_article", onDelete: "CASCADE", nullable: true)] // supprimer le contenu d'un article supprime les noeuds associés, faux dans l'autre sens
40 private ?Article $article = null;
41
42 // propriété non mappée dans la table "node", la jointure est décrite dans NodeData
43 // elle sert à persister ou supprimer des données par cascade
44 // "mappedBy" permet de cibler $node dans l'autre classe, qui elle possède un "inversedBy"
45 #[ORM\OneToOne(targetEntity: NodeData::class, mappedBy: "node", cascade: ['persist', 'remove'])]
46 private ?NodeData $node_data = null;
47
48 #[ORM\OneToOne(targetEntity: EmailForm::class, mappedBy: "node", cascade: ['persist'])] // pas de remove, les e-mails sont associés au EmailForm
49 private ?EmailForm $email_form = null;
50
51 #[ORM\OneToOne(targetEntity: Presentation::class, mappedBy: "node", cascade: ['persist', 'remove'])]
52 private ?Presentation $presentation = null;
53
54 // attributs non destinés à doctrine
55 private array $children = []; // tableau de Node
56 private ?self $adopted = null; // = "new" est un enfant de "main" lorsque la page est "article"
57
58 public function __construct(string $name = '', int $position = 0, ?self $parent = null, ?Page $page = null, ?Article $article = null){
59 $this->name_node = $name;
60 $this->position = $position;
61 $this->parent = $parent;
62 $this->page = $page;
63 $this->article = $article;
64 }
65
66 // pfff...
67 public function getId(): ?int
68 {
69 return $this->id_node;
70 }
71 public function getName(): string
72 {
73 return $this->name_node;
74 }
75 /*public function setName(string $name): void
76 {
77 $this->name_node = $name;
78 }*/
79
80 public function getParent(): ?self
81 {
82 return $this->parent;
83 }
84 /*public function setParent(?self $parent): void
85 {
86 $this->parent = $parent;
87 }*/
88 public function getPosition(): int
89 {
90 return $this->position;
91 }
92 public function setPosition(int $position): void
93 {
94 $this->position = $position;
95 }
96 public function getPage(): Page
97 {
98 return $this->page;
99 }
100 /*public function setPage(Page $page): void
101 {
102 $this->page = $page;
103 }*/
104
105 // utiliser l'interface de NodeData et EmailForm
106 public function getArticle(): Article
107 {
108 return $this->article;
109 }
110 /*public function setArticle(Article $article): void
111 {
112 $this->article = $article;
113 }*/
114
115 // une interface serait cool!
116 // OU possible un "héritage doctrine", faire en sorte que Node soit la mère de NodeData, EmailForm, Article...
117 // une seule instance est matérialisée par plusieurs tables en même temps
118 public function getNodeData(): NodeData|EmailForm|Presentation|null
119 {
120 // pour du polymorphisme (une unique variable $node_data): opter pour l'héritage
121 return match($this->name_node){
122 'form' => $this->email_form,
123 'post_block', 'news_block' => $this->presentation,
124 default => $this->node_data, // inclut 'calendar'
125 };
126 }
127 public function getChildren(): array
128 {
129 return $this->children;
130 }
131 public function getNodeByName(string $name): ?Node
132 {
133 foreach($this->children as $child){
134 if($child->getName() === $name){
135 return $child;
136 }
137 }
138 return null;
139 }
140 public function addChild(self $child): void
141 {
142 $this->children[] = $child;
143
144 if(!Presentation::hasPresentation($this->getName())){ // post_block et news_block ont leurs enfants ordonnés avec ORDER BY
145 $this->sortChildren(false);
146 }
147 }
148 public function removeChild(self $child): void
149 {
150 foreach($this->children as $key => $object){
151 if($object->getId() === $child->getId()){
152 unset($this->children[$key]);
153 }
154 break;
155 }
156 $this->children = array_values($this->children); // réindexer pour supprimer la case vide
157 }
158
159 public function getAdoptedChild(): ?self // peut renvoyer null
160 {
161 return $this->adopted;
162 }
163 public function setAdoptedChild(self $child): void
164 {
165 $this->adopted = $child;
166 }
167}
diff --git a/src/Model/Entity/NodeData.php b/src/Model/Entity/NodeData.php
new file mode 100644
index 0000000..d81628d
--- /dev/null
+++ b/src/Model/Entity/NodeData.php
@@ -0,0 +1,102 @@
1<?php
2// src/Model/Entity/NodeData.php
3
4declare(strict_types=1);
5
6namespace App\Entity;
7
8use Doctrine\ORM\Mapping as ORM;
9use Doctrine\Common\Collections\ArrayCollection; // classe
10use Doctrine\Common\Collections\Collection; // interface
11
12#[ORM\Entity]
13#[ORM\Table(name: TABLE_PREFIX . "node_data")]
14class NodeData
15{
16 static array $social_networks = ['globe', 'facebook', 'instagram', 'whatsapp', 'snapchat', 'tiktok', 'linkedin', 'github']; // à completer
17
18 #[ORM\Id]
19 #[ORM\GeneratedValue]
20 #[ORM\Column(type: "integer")]
21 private int $id_node_data;
22
23 // onDelete: "CASCADE" supprime les données si le noeud est supprimé
24 // inverseBy fait le lien avec $node_data dans Node (qui a "mappedBy")
25 #[ORM\OneToOne(targetEntity: Node::class, inversedBy: "node_data")]
26 #[ORM\JoinColumn(name: "node_id", referencedColumnName: "id_node", onDelete: "CASCADE")]
27 private ?Node $node;
28
29 #[ORM\Column(type: "json")]
30 private array $data;
31
32 #[ORM\OneToMany(mappedBy: 'node_data', targetEntity: AssetEmployment::class, cascade: ['persist', 'remove'])]
33 private Collection $asset_employment;
34
35 private array $emails = []; // => noeud "show_emails"
36
37 public function __construct(array $data, Node $node, Collection $asset_employment = new ArrayCollection){
38 $this->data = $data;
39 $this->node = $node;
40 $this->asset_employment = $asset_employment;
41 }
42
43 public function getId(): int
44 {
45 return $this->id_node_data;
46 }
47 public function getData(): array
48 {
49 return $this->data;
50 }
51 /*public function setData(array $data): void // entrée = tableau associatif
52 {
53 $this->data = $data;
54 }*/
55 public function updateData(string $key, string|int|bool|array $value = ''): void
56 {
57 if($value !== ''){
58 $this->data[$key] = $value;
59 }
60 // si $value est vide, supprime la clé
61 elseif(isset($this->data[$key])){
62 unset($this->data[$key]);
63 }
64 }
65
66 public function setNode(?Node $node): void
67 {
68 $this->node = $node;
69 }
70
71 public function getNodeDataAssets(): Collection
72 {
73 return $this->asset_employment;
74 }
75 public function getAssetEmploymentByRole(string $role): ?AssetEmployment
76 {
77 foreach($this->asset_employment as $nda){
78 if($nda->getRole() === $role){
79 return $nda;
80 }
81 }
82 return null;
83 }
84 public function getAssetByRole(string $role): ?Asset
85 {
86 $nda = $this->getAssetEmploymentByRole($role);
87 if($nda === null){
88 return null;
89 }
90 return $nda->getAsset() ?? null;
91 }
92
93 // pour affichage page Courriels
94 public function getEmails(): array // appelée dans ShowEmailsBuilder
95 {
96 return $this->emails;
97 }
98 public function setEmails(array $emails): void // appelée dans Model
99 {
100 $this->emails = $emails;
101 }
102} \ No newline at end of file
diff --git a/src/Model/Entity/Page.php b/src/Model/Entity/Page.php
new file mode 100644
index 0000000..7cca3dc
--- /dev/null
+++ b/src/Model/Entity/Page.php
@@ -0,0 +1,222 @@
1<?php
2// src/Model/Entity/Page.php
3
4declare(strict_types=1);
5
6namespace App\Entity;
7
8use Doctrine\ORM\Mapping as ORM;
9use Doctrine\Common\Collections\Collection;
10use Doctrine\Common\Collections\ArrayCollection;
11use App\Model\Position;
12
13#[ORM\Entity]
14#[ORM\Table(name: TABLE_PREFIX . "page")]
15class Page
16{
17 use Position;
18
19 #[ORM\Id]
20 #[ORM\GeneratedValue]
21 #[ORM\Column(type: "integer")]
22 private int $id_page;
23
24 #[ORM\Column(type: "string", length: 255)]
25 private string $name_page;
26
27 #[ORM\Column(type: "string", length: 255)]
28 private string $end_of_path; // morceau d'URL plus exactement
29
30 private string $page_path = '';
31
32 #[ORM\Column(type: "text")]
33 private string $description;
34
35 #[ORM\Column(type: "json", nullable: true)]
36 private ?array $css = null;
37
38 #[ORM\Column(type: "json", nullable: true)]
39 private ?array $js = null;
40
41 static private array $default_css = ['body', 'head', 'nav', 'foot'];
42 static private array $default_js = ['main'];
43
44 #[ORM\Column(type: "boolean")]
45 private bool $reachable;
46
47 #[ORM\Column(type: "boolean")]
48 private bool $in_menu;
49
50 #[ORM\Column(type: "boolean")]
51 private bool $hidden;
52
53 #[ORM\Column(type: "integer", nullable: true)] // null si hors menu
54 private ?int $position;
55
56 #[ORM\ManyToOne(targetEntity: self::class, inversedBy: "children")]
57 #[ORM\JoinColumn(name: "parent_id", referencedColumnName: "id_page", onDelete: "SET NULL", nullable: true)]
58 private ?self $parent = null;
59
60 #[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent')]
61 protected Collection $children;
62
63 /*#[ORM\Column(type: "json", nullable: true)]
64 private ?array $metadata = null;*/
65
66 public function __construct(string $name, string $eop, string $description, bool $reachable, bool $in_menu, bool $hidden, ?int $position, ?Page $parent)
67 {
68 $this->name_page = $name;
69 $this->end_of_path = $eop;
70 $this->description = $description;
71 $this->reachable = $reachable;
72 $this->in_menu = $in_menu;
73 $this->hidden = $hidden;
74 $this->position = $position;
75 $this->parent = $parent;
76 $this->children = new ArrayCollection();
77 }
78
79 // getters/setters
80 public function getId(): int
81 {
82 return $this->id_page;
83 }
84 public function getPageName(): string
85 {
86 return $this->name_page;
87 }
88 public function setPageName(string $name): void
89 {
90 $this->name_page = $name;
91 }
92 public function getPagePath(): string
93 {
94 return $this->page_path;
95 }
96 public function setPagePath(string $path):void
97 {
98 $this->page_path = $path;
99 }
100 public function getEndOfPath(): string
101 {
102 return $this->end_of_path;
103 }
104 public function setEndOfPath(string $end_of_path):void
105 {
106 $this->end_of_path = $end_of_path;
107 }
108 public function getDescription(): string
109 {
110 return $this->description;
111 }
112 public function setDescription(string $description): void
113 {
114 $this->description = $description;
115 }
116
117 public function getCSS(): array
118 {
119 return array_merge(self::$default_css, $this->css ?? []);
120 }
121 public function addCSS(string $css): void
122 {
123 if(!in_array($css, $this->css ?? [])){
124 $this->css[] = $css;
125 }
126 }
127 public function removeCSS(string $css): void
128 {
129 // array_diff renvoie une copie du 1er tableau alégée des élements existants aussi dans le 2è, array_values réindexe
130 $this->css = array_values(array_diff($this->css, [$css]));
131 }
132 public function getJS(): array
133 {
134 return array_merge(self::$default_js, $this->js ?? []);
135 //UPDATE `nb_page` SET `js` = NULL WHERE JSON_EQUALS(`js`, '["main"]');
136 }
137 public function addJS(string $js): void
138 {
139 if(!in_array($js, $this->js ?? [])){
140 $this->js[] = $js;
141 }
142 }
143 public function removeJS(string $js): void
144 {
145 $this->js = array_values(array_diff($this->js, [$js]));
146 }
147
148 public function isReachable(): bool
149 {
150 return $this->reachable;
151 }
152 public function isInMenu(): bool
153 {
154 return $this->in_menu;
155 }
156 public function isHidden(): bool
157 {
158 return $this->hidden;
159 }
160 public function setHidden(bool $hidden): void
161 {
162 $this->hidden = $hidden;
163 }
164 public function getPosition(): ?int
165 {
166 return $this->position;
167 }
168 public function setPosition(int $position): void
169 {
170 $this->position = $position;
171 }
172 public function getParent(): ?Page
173 {
174 return $this->parent;
175 }
176 public function setParent(?Page $parent): void
177 {
178 $this->parent = $parent;
179 }
180 public function getChildren(): Collection
181 {
182 return $this->children;
183 }
184
185 // utilisée par $menu_path
186 public function fillChildrenPagePath(string $parent_path = ''): void
187 {
188 $this->page_path = $parent_path != '' ? $parent_path . '/' . $this->end_of_path : $this->end_of_path;
189 foreach($this->getChildren() as $page){
190 $page->fillChildrenPagePath($this->page_path);
191 }
192 }
193
194 public function addChild(self $child): void
195 {
196 $this->children[] = $child;
197 $this->sortChildren(false);
198 }
199 public function removeChild(self $child): void
200 {
201 $this->children->removeElement($child);
202 $this->children = new ArrayCollection(array_values($this->children->toArray())); // réindexer en passant par un tableau
203 $this->sortChildren(false);
204 }
205
206 public function findPageById(int $id): ?Page
207 {
208 $target = null;
209 foreach($this->children as $page){
210 if($page->getId() === $id){
211 return $page;
212 }
213 if(count($page->getChildren()) > 0){
214 $target = $page->findPageById($id);
215 if($target !== null){
216 return $target;
217 }
218 }
219 }
220 return $target;
221 }
222}
diff --git a/src/Model/Entity/Presentation.php b/src/Model/Entity/Presentation.php
new file mode 100644
index 0000000..f5e3502
--- /dev/null
+++ b/src/Model/Entity/Presentation.php
@@ -0,0 +1,135 @@
1<?php
2// src/Model/Entity/Presentation.php
3
4declare(strict_types=1);
5
6namespace App\Entity;
7
8use Doctrine\ORM\Mapping as ORM;
9
10#[ORM\Entity]
11#[ORM\Table(name: TABLE_PREFIX . "presentation")]
12class Presentation{
13 #[ORM\Id]
14 #[ORM\GeneratedValue]
15 #[ORM\Column(type: "integer")]
16 private int $id_presentation;
17
18 // inverseBy fait le lien avec $presentation dans Node (qui a "mappedBy")
19 #[ORM\OneToOne(targetEntity: Node::class, inversedBy: "presentation")]
20 #[ORM\JoinColumn(name: "node_id", referencedColumnName: "id_node")]
21 private ?Node $node;
22
23 #[ORM\Column(type: "json")]
24 private array $data;
25
26 #[ORM\Column(type: "string", length: 255, nullable: true)]
27 private ?string $presentation;
28
29 #[ORM\Column(type: "boolean", length: 255, nullable: true)]
30 private ?bool $chrono_order = null;
31
32 #[ORM\Column(type: "integer", nullable: true)]
33 private ?int $grid_cols_min_width = null; // pour le mode grille
34
35 #[ORM\Column(type: "integer", nullable: true)]
36 private ?int $pagination_limit = null; // pour les post_block et news_block
37
38 private int $nb_pages = 1;
39
40 public function __construct(array $data, Node $node
41 //, ?string $presentation = null, ?bool $chrono_order = null
42 ){
43 $this->data = $data;
44 $this->node = $node;
45 /*if(!empty($presentation) && $presentation === 'grid'){
46 $this->grid_cols_min_width = 250;
47 }
48 $this->chrono_order = $chrono_order ?? null;*/
49 }
50
51 /*public function getId(): int
52 {
53 return $this->id_presentation;
54 }*/
55
56 public function setNode(?Node $node): void
57 {
58 $this->node = $node;
59 }
60
61 // un trait pour ça
62 public function getData(): array
63 {
64 return $this->data;
65 }
66 public function updateData(string $key, string|int|bool|array $value = ''): void
67 {
68 if($value !== ''){
69 $this->data[$key] = $value;
70 }
71 // si $value est vide, supprime la clé
72 elseif(isset($this->data[$key])){
73 unset($this->data[$key]);
74 }
75 }
76
77 // spécifique aux blocs contenant des articles
78 public function getPresentation(): ?string
79 {
80 return $this->presentation;
81 }
82 public function setPresentation(string $presentation): void
83 {
84 $this->presentation = $presentation;
85 }
86 public function getColsMinWidth(): int
87 {
88 $default = 320; // pixels
89 return $this->grid_cols_min_width === null ? $default : $this->grid_cols_min_width;
90 }
91 public function setColsMinWidth(int $columns): void
92 {
93 $this->grid_cols_min_width = $columns;
94 }
95 public function getChronoOrder(): bool
96 {
97 return $this->chrono_order ?? false;
98 }
99 public function setChronoOrder(bool $reverse_order): void
100 {
101 $this->chrono_order = $reverse_order;
102 }
103
104 public function getPaginationLimit(): ?int
105 {
106 $default = 12; // si 0 pas de pagination, 12 rend bien avec des grilles de 2, 3 ou 4 colonnes
107 return $this->pagination_limit === null ? $default : $this->pagination_limit;
108 }
109 public function setPaginationLimit(int $pagination_limit): void
110 {
111 $this->pagination_limit = $pagination_limit;
112 }
113 public function getNumberOfPages(): int
114 {
115 return $this->nb_pages;
116 }
117 public function setNumberOfPages(int $nb_pages): void
118 {
119 $this->nb_pages = $nb_pages;
120 }
121
122
123 /* -- blocs et présentations disponibles dans l'application -- */
124 const BLOCKS = ['post_block' => 'Articles libres', 'news_block' => 'Actualités',
125 //'galery' => 'Galerie',
126 'calendar' => 'Calendrier', 'form' => 'Formulaire'
127 ];
128 const PRESENTATIONS = ['fullwidth' => 'Pleine largeur', 'grid' => 'Grille', 'mosaic' => 'Mosaïque'
129 //, 'carousel' => 'Carrousel'
130 ];
131 static public function hasPresentation(string $block): bool
132 {
133 return in_array($block, ['post_block', 'news_block']) ? true : false;
134 }
135} \ No newline at end of file
diff --git a/src/Model/Entity/User.php b/src/Model/Entity/User.php
new file mode 100644
index 0000000..461a5bc
--- /dev/null
+++ b/src/Model/Entity/User.php
@@ -0,0 +1,67 @@
1<?php
2// src/Model/Entity/User.php
3
4declare(strict_types=1);
5
6namespace App\Entity;
7
8use Doctrine\ORM\Mapping as ORM;
9
10#[ORM\Entity]
11#[ORM\Table(name: TABLE_PREFIX . "user")]
12class User
13{
14 #[ORM\Id]
15 #[ORM\GeneratedValue]
16 #[ORM\Column(type: "integer")]
17 private int $id_user;
18
19 /*#[ORM\Column(type: "string", length: 255)]
20 private string $name;*/
21
22 #[ORM\Column(type: "string", length: 255, unique: true)] // risque de modifier son mot de passe sans s'apercevoir qu'il fonctionne encore sur un autre compte
23 private string $login;
24
25 #[ORM\Column(type: "string", length: 50)]
26 private string $role;
27
28 #[ORM\Column(type: "string", length: 255)]
29 private string $password;
30
31 public function __construct(string $login, string $role, string $password)
32 {
33 $this->login = $login;
34 $this->role = $role;
35 $this->password = $password;
36 }
37
38 public function getId(): int
39 {
40 return $this->id_user;
41 }
42
43 public function getLogin(): string
44 {
45 return $this->login;
46 }
47 public function setLogin(string $login): void
48 {
49 $this->login = $login;
50 }
51 public function getRole(): string
52 {
53 return $this->role;
54 }
55 public function setRole(string $role): void
56 {
57 $this->role = $role;
58 }
59 public function getPassword(): string
60 {
61 return $this->password;
62 }
63 public function setPassword(string $password): void
64 {
65 $this->password = $password;
66 }
67} \ No newline at end of file