Crvi/app/models/Traducteur_Model.php
2026-01-20 07:54:37 +01:00

579 lines
21 KiB
PHP

<?php
declare(strict_types=1);
namespace ESI_CRVI_AGENDA\models;
use ESI_CRVI_AGENDA\helpers\Api_Helper;
class CRVI_Traducteur_Model extends Main_Model {
public $id;
public $nom;
public $prenom;
public $email;
public $langues_parlees;
public $jours_de_disponibilite;
public $indisponibilitee_ponctuelle;
public $organisme;
public $commentaires;
public $type_de_fiche;
/**
* Carte des codes de langue vers le nom de la langue (Europe continentale + Afrique du Nord)
*/
public static $lg_map = [
'fr' => 'Français',
'de' => 'Allemand',
'it' => 'Italien',
'es' => 'Espagnol',
'pt' => 'Portugais',
'nl' => 'Néerlandais',
'en' => 'Anglais',
'ru' => 'Russe',
'pl' => 'Polonais',
'ro' => 'Roumain',
'el' => 'Grec',
'tr' => 'Turc',
'ar' => 'Arabe',
'kab' => 'Kabyle',
'ber' => 'Berbère',
'tzm' => 'Tamazight',
'da' => 'Danois',
'sv' => 'Suédois',
'no' => 'Norvégien',
'fi' => 'Finnois',
'cs' => 'Tchèque',
'sk' => 'Slovaque',
'hu' => 'Hongrois',
'bg' => 'Bulgare',
'hr' => 'Croate',
'sr' => 'Serbe',
'bs' => 'Bosnien',
'sq' => 'Albanais',
'mk' => 'Macédonien',
'sl' => 'Slovène',
'he' => 'Hébreu',
'lt' => 'Lituanien',
'lv' => 'Letton',
'et' => 'Estonien',
];
/**
* Schéma des champs ACF : nom => type (ex : group, repeater, taxonomy, text...)
*/
public static $acf_schema = [
'nom' => 'text',
'prenom' => 'text',
'email' => 'email',
'organisme' => 'taxonomy',
'langues_parlees' => 'taxonomy',
'jours_de_disponibilite' => 'checkbox',
'indisponibilitee_ponctuelle' => 'repeater',
'coordonnees' => 'group',
'commentaires' => 'textarea',
'type_de_fiche' => 'select',
];
public function __construct($data = []) {
foreach ($data as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
}
public static function get_traducteurs($filters = [],$simple_list = false) {
$posts = get_posts([
'post_type' => 'traducteur',
'numberposts' => -1,
'meta_query' => $filters,
]);
if ($simple_list) {
$posts = array_map(function($post) {
return [
'id' => $post->ID,
'nom' => $post->post_title,
];
}, $posts);
}
return $posts;
}
public static function load($id, $fields = []) {
// Charger depuis CPT/meta
$traducteur = get_post($id);
if (!$traducteur) {
return null;
}
// Si des champs spécifiques sont demandés, ne charger que ceux-ci
if (!empty($fields)) {
$data = [];
foreach ($fields as $field) {
if ($field === 'id') {
$data['id'] = $traducteur->ID;
} elseif (property_exists(self::class, $field)) {
$data[$field] = get_field($field, $traducteur->ID);
}
}
return new self($data);
}
// Sinon, charger tous les champs par défaut
return new self([
'id' => $traducteur->ID,
'nom' => get_field('nom', $traducteur->ID),
'prenom' => get_field('prenom', $traducteur->ID),
'email' => get_field('email', $traducteur->ID),
'langues_parlees' => get_field('langues_parlees', $traducteur->ID),
'jours_de_disponibilite' => get_field('jours_de_disponibilite', $traducteur->ID),
'indisponibilitee_ponctuelle' => get_field('indisponibilitee_ponctuelle', $traducteur->ID),
'organisme' => get_field('organisme', $traducteur->ID),
'commentaires' => get_field('commentaires', $traducteur->ID),
'type_de_fiche' => get_field('type_de_fiche', $traducteur->ID),
]);
}
public function save() {
// À implémenter : sauvegarder dans CPT/meta
return true;
}
/**
* Retourne les traducteurs disponibles à une date donnée et pour une langue donnée.
* @param string $date au format Y-m-d
* @param string $langue_slug (slug de la langue)
* @param int|null $event_id ID de l'événement en cours d'édition (pour inclure le traducteur actuel)
* @return array Liste des traducteurs disponibles (CRVI_Traducteur_Model)
*/
public static function filtrer_disponibles($date, $langue_slug, $event_id = null) {
if (empty($date)) {
return [];
}
// Construire la requête de base
$args = [
'post_type' => 'traducteur',
'numberposts' => -1,
];
// Ajouter le filtre de langue seulement si spécifié
if (!empty($langue_slug)) {
$args['tax_query'] = [
[
'taxonomy' => 'langue',
'field' => 'slug',
'terms' => $langue_slug,
],
];
}
$traducteurs = get_posts($args);
$disponibles = [];
foreach ($traducteurs as $traducteur_post) {
$traducteur = new self([
'id' => $traducteur_post->ID,
'nom' => get_field('nom', $traducteur_post->ID),
'prenom' => get_field('prenom', $traducteur_post->ID),
'email' => get_field('email', $traducteur_post->ID),
'langues_parlees' => get_field('langues_parlees', $traducteur_post->ID),
'jours_de_disponibilite' => get_field('jours_de_disponibilite', $traducteur_post->ID),
'indisponibilitee_ponctuelle' => get_field('indisponibilitee_ponctuelle', $traducteur_post->ID),
'organisme' => get_field('organisme', $traducteur_post->ID),
'commentaires' => get_field('commentaires', $traducteur_post->ID),
'type_de_fiche' => get_field('type_de_fiche', $traducteur_post->ID),
]);
// Vérifier si le traducteur est disponible
$is_disponible = $traducteur->is_disponible($traducteur->id, $date, $langue_slug);
// Vérifier les conflits d'événements existants
if ($is_disponible) {
$conflits = self::verifier_conflits_traducteur($traducteur->id, $date, $event_id);
if (!empty($conflits)) {
$is_disponible = false;
}
}
// Si c'est l'événement en cours d'édition, inclure le traducteur même s'il est "pris"
if ($event_id && self::is_traducteur_of_event($traducteur->id, $event_id)) {
$is_disponible = true;
}
if ($is_disponible) {
$disponibles[] = $traducteur;
}
}
return $disponibles;
}
/**
* Vérifie si un traducteur est associé à un événement donné
* @param int $traducteur_id
* @param int $event_id
* @return bool
*/
private static function is_traducteur_of_event($traducteur_id, $event_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'crvi_agenda';
$result = $wpdb->get_var($wpdb->prepare(
"SELECT id FROM $table_name WHERE id = %d AND id_traducteur = %d",
$event_id,
$traducteur_id
));
return !empty($result);
}
/**
* Vérifie si ce traducteur est disponible à une date donnée et pour une langue donnée.
* @param string $date au format Y-m-d
* @param string|null $langue_slug
* @return bool
*/
public function is_disponible($traducteur_id, $date, $langue_slug = null) {
// Vérifier que la date n'est pas null ou vide
if (empty($date)) {
return false;
}
$timestamp = strtotime($date);
if ($timestamp === false) {
return false;
}
$jour = strtolower(date('l', $timestamp));
$jours_disponibles = get_field('jours_de_disponibilite', 'user_' . $traducteur_id);
if (!is_array($jours_disponibles) || !in_array($jour, $jours_disponibles, true)) {
return false;
}
$indisponibilites = get_field('indisponibilitee_ponctuelle', 'user_' . $traducteur_id);
if (is_array($indisponibilites)) {
foreach ($indisponibilites as $absence) {
$debut = isset($absence['debut']) ? strtotime($absence['debut']) : null;
$fin = isset($absence['fin']) ? strtotime($absence['fin']) : null;
if ($debut && $fin && $timestamp >= $debut && $timestamp <= $fin) {
return false;
}
}
}
// Optionnel : vérifier la langue si besoin
if ($langue_slug && is_array($this->langues_parlees) && !in_array($langue_slug, $this->langues_parlees)) {
return false;
}
return true;
}
public function get_relations() {
// À implémenter : récupérer les rendez-vous associés, etc.
return [];
}
/**
* Créer un traducteur.
* @param array $data
* @return int|WP_Error
*/
public static function create(array $data, bool $as_rest = false) {
if (empty($data['nom']) || empty($data['prenom']) || empty($data['email'])) {
if ($as_rest) {
return [
'success' => false,
'code' => 400,
'message' => 'Champs obligatoires manquants',
'data' => null
];
}
return false;
}
// Chercher un traducteur existant par email
$existing = get_posts([
'post_type' => 'traducteur',
'meta_key' => 'email',
'meta_value' => $data['email'],
'post_status'=> 'any',
'numberposts'=> 1,
]);
if ($existing) {
// Mise à jour
$post_id = $existing[0]->ID;
// Mettre à jour le titre si nom/prenom fournis
$post_title = ($data['nom'] ?? get_post_meta($post_id, 'nom', true)) . ' ' . ($data['prenom'] ?? get_post_meta($post_id, 'prenom', true));
wp_update_post([
'ID' => $post_id,
'post_title' => $post_title,
]);
foreach ($data as $key => $value) {
if ($key === 'organisme') {
if (!taxonomy_exists('organisme')) {
register_taxonomy(
'organisme',
'traducteur',
[
'label' => 'Organismes',
'public' => false,
'hierarchical' => false,
'show_ui' => true,
'show_in_rest' => false,
]
);
}
$org_ids = [];
$organismes = is_array($value) ? $value : explode('|', $value);
foreach ($organismes as $org_nom) {
$org_nom = trim($org_nom);
if (empty($org_nom)) continue;
$slug = sanitize_title($org_nom);
$term = get_term_by('slug', $slug, 'organisme');
if (!$term) {
$result = wp_insert_term($org_nom, 'organisme', ['slug' => $slug]);
if (!is_wp_error($result) && isset($result['term_id'])) {
$org_ids[] = $result['term_id'];
}
} else {
$org_ids[] = $term->term_id;
}
}
if (!empty($org_ids)) {
update_field('organisme', $org_ids, $post_id);
}
} else if ($key === 'langues_parlees') {
if (!taxonomy_exists('langue')) {
register_taxonomy(
'langue',
'traducteur',
[
'label' => 'Langues',
'public' => false,
'hierarchical' => false,
'show_ui' => true,
'show_in_rest' => false,
]
);
}
$langue_ids = [];
$langues = is_array($value) ? $value : explode('|', $value);
foreach ($langues as $slug) {
$slug = trim($slug);
if (empty($slug)) continue;
$term = get_term_by('slug', $slug, 'langue');
if (!$term) {
$nom = self::$lg_map[$slug] ?? $slug;
$result = wp_insert_term($nom, 'langue', ['slug' => $slug]);
if (!is_wp_error($result) && isset($result['term_id'])) {
$langue_ids[] = $result['term_id'];
}
} else {
$langue_ids[] = $term->term_id;
}
}
if (!empty($langue_ids)) {
update_field('langue', $langue_ids, $post_id);
}
} else if (isset(self::$acf_schema[$key])) {
self::set_acf_field($post_id, $key, $value, self::$acf_schema);
} else {
update_post_meta($post_id, $key, $value);
}
}
return $post_id;
}
// Création classique
$post_id = wp_insert_post([
'post_type' => 'traducteur',
'post_title' => $data['nom'] . ' ' . $data['prenom'],
'post_status' => 'publish',
]);
if (is_wp_error($post_id)) {
if ($as_rest) {
return [
'success' => false,
'code' => 500,
'message' => 'Erreur lors de la création',
'data' => json_encode($post_id)
];
}
return $post_id;
}
foreach ($data as $key => $value) {
if ($key === 'organisme') {
if (!taxonomy_exists('organisme')) {
register_taxonomy(
'organisme',
'traducteur',
[
'label' => 'Organismes',
'public' => false,
'hierarchical' => false,
'show_ui' => true,
'show_in_rest' => false,
]
);
}
$org_ids = [];
$organismes = is_array($value) ? $value : explode('|', $value);
foreach ($organismes as $org_nom) {
$org_nom = trim($org_nom);
if (empty($org_nom)) continue;
$slug = sanitize_title($org_nom);
$term = get_term_by('slug', $slug, 'organisme');
if (!$term) {
$result = wp_insert_term($org_nom, 'organisme', ['slug' => $slug]);
if (!is_wp_error($result) && isset($result['term_id'])) {
$org_ids[] = $result['term_id'];
}
} else {
$org_ids[] = $term->term_id;
}
}
if (!empty($org_ids)) {
update_field('organisme', $org_ids, $post_id);
}
} else if ($key === 'langues_parlees') {
if (!taxonomy_exists('langue')) {
register_taxonomy(
'langue',
'traducteur',
[
'label' => 'Langues',
'public' => false,
'hierarchical' => false,
'show_ui' => true,
'show_in_rest' => false,
]
);
}
$langue_ids = [];
$langues = is_array($value) ? $value : explode('|', $value);
foreach ($langues as $slug) {
$slug = trim($slug);
if (empty($slug)) continue;
$term = get_term_by('slug', $slug, 'langue');
if (!$term) {
$nom = self::$lg_map[$slug] ?? $slug;
$result = wp_insert_term($nom, 'langue', ['slug' => $slug]);
if (!is_wp_error($result) && isset($result['term_id'])) {
$langue_ids[] = $result['term_id'];
}
} else {
$langue_ids[] = $term->term_id;
}
}
if (!empty($langue_ids)) {
update_field('langue', $langue_ids, $post_id);
}
} else if (isset(self::$acf_schema[$key])) {
self::set_acf_field($post_id, $key, $value, self::$acf_schema);
} else {
update_post_meta($post_id, $key, $value);
}
}
return $post_id;
}
/**
* Mettre à jour un traducteur.
* @param int $id
* @param array $data
* @return bool|WP_Error
*/
public function update(int $id, array $data) {
if (!is_user_logged_in() || !get_current_user_id()) {
return Api_Helper::json_error('Authentification requise', 401);
}
if (!current_user_can('edit_posts')) {
return Api_Helper::json_error('Non autorisé', 403);
}
$post = get_post($id);
if (!$post || $post->post_type !== 'traducteur') {
return Api_Helper::json_error('Traducteur introuvable', 404);
}
// Vérifier unicité de l'email si modifié
if (!empty($data['email'])) {
$existing = get_posts([
'post_type' => 'traducteur',
'meta_key' => 'email',
'meta_value' => $data['email'],
'post_status'=> 'any',
'exclude' => [$id],
'numberposts'=> 1,
]);
if ($existing) {
return Api_Helper::json_error('Email déjà utilisé', 409);
}
}
$result = wp_update_post([
'ID' => $id,
'post_title' => ($data['nom'] ?? get_post_meta($id, 'nom', true)) . ' ' . ($data['prenom'] ?? get_post_meta($id, 'prenom', true)),
], true);
if (is_wp_error($result)) {
return $result;
}
foreach ($data as $key => $value) {
update_post_meta($id, $key, $value);
}
return true;
}
/**
* Supprimer un traducteur (corbeille WordPress)
* @param int $id
* @return bool|WP_Error
*/
public function delete(int $id) {
if (!is_user_logged_in() || !get_current_user_id()) {
return Api_Helper::json_error('Authentification requise', 401);
}
if (!current_user_can('delete_posts')) {
return Api_Helper::json_error('Non autorisé', 403);
}
$post = get_post($id);
if (!$post || $post->post_type !== 'traducteur') {
return Api_Helper::json_error('Traducteur introuvable', 404);
}
$result = wp_trash_post($id);
if (!$result) {
return Api_Helper::json_error('Erreur lors de la suppression', 500);
}
return true;
}
/**
* Vérifie s'il y a des conflits d'événements pour un traducteur donné
* @param int $traducteur_id
* @param string $date
* @param int|null $event_id - ID de l'événement à exclure (pour l'édition)
* @return array
*/
private static function verifier_conflits_traducteur($traducteur_id, $date, $event_id = null) {
global $wpdb;
$table_events = $wpdb->prefix . 'crvi_agenda';
$where_conditions = ['id_traducteur = %d'];
$where_values = [$traducteur_id];
// Exclure l'événement en cours d'édition si spécifié
if ($event_id) {
$where_conditions[] = 'id != %d';
$where_values[] = $event_id;
}
// Ajouter la condition de date
$where_conditions[] = 'date_rdv = %s';
$where_values[] = $date;
$where_clause = implode(' AND ', $where_conditions);
$query = $wpdb->prepare(
"SELECT * FROM {$table_events} WHERE {$where_clause}",
$where_values
);
return $wpdb->get_results($query, ARRAY_A);
}
}