Je me permet de vous poster le code pour savoir ce que vous en pensez si j'ai bien fait. Je voulais utiliser deux tables pour minimiser les requêtes SQL et les jointures. Je pars donc sur deux tables :

J'ai appelé cette table mainMessages mais en gros on peut l'appeler topics (c'est juste qu'il va y avoir un forum plus tard et j'étais peu inspiré bref). Rien de bien sorcier pour cette table
- users contient les utilisateurs de la conversation en json (nom et id)
- pareil pour read mais ajoute un boolean read pour savoir si l'utilisateur a vu le dernier message ou non
Ensuite une table classique messages :

Pour ce qui est de l'ajout du message, il y a un système de requêtes en ajax pour trouver les utilisateurs. Quand un utilisateur est ajouté dans la covnersation, un champ de type hidden se met à jour avec les données id et username en format json de la conversation. à partir de là j'utilise le code suivant pour créer la conversation et ajouter le message à la conversation :
public function add()
{
$this->MainMessages = TableRegistry::get("MainMessages");
// Le code qui me permet d'obtenir en ajax les utilisateurs pour les récupérer dans la vue et es ajouter à la conversation
if($this->request->is('ajax') && isset($this->request->query['name']))
{
$name = $this->request->query['name'];
$this->autoRender = false;
$users = TableRegistry::get('Users');
$user = $users->find('all', ['limit' => 5, 'fields' => ['Users.username', 'Users.id'], 'conditions' => ['Users.username LIKE' => "%$name%"]]);
$this->response->type('json');
echo json_encode($user);
}
if($this->request->is("post"))
{
// On récupère les utilisateurs dans le champ caché au format json
$datas = $this->request->data;
$users = json_decode($datas['users']);
// On ajoute l'utilisateur courant au json
$u = new \stdClass();
$u->username = $this->Auth->user('username');
$u->id = $this->Auth->user('id');
$users[] = $u;
$this->request->data['users'] = json_encode($users);
// On parcours les utilisateurs de la conversation pour dire qu'aucun n'a lu le message de la conversation
// Sauf pour celui qui a créé la conversation
$arr = [];
foreach($users as $user)
{
if($user->id !== $this->Auth->user('id'))
{
$c = new \stdClass();
$c->id = $user->id;
$c->username = $user->username;
$c->readed = 0;
$arr[] = $c;
}
}
$messages = TableRegistry::get("Messages");
$message = $messages->newEntity([
'content' => $this->request->data['content'],
'user_id' => $this->Auth->user('id')
]);
// On sauvegarde le message
if(!$messages->save($message))
$this->Flash->error($message->errors());
// On sauvegarde a conversation
$this->request->data['read'] = json_encode($arr);
$this->request->data['user_id'] = $this->Auth->user('id');
$this->request->data['last'] = $message->id;
$mainMessage = $this->MainMessages->newEntity($this->request->data);
if($this->MainMessages->save($mainMessage))
{
$this->Flash->success("La discution a bien été ajoutée");
$this->redirect(['action' => 'index']);
}
else
$this->Flash->error($mainMessage->errors());
}
}
à ce niveau là on récupère facilement les conversations de l'utilisateur :
$id = $this->Auth->user('id');
$messages = $this->MainMessages->find('all', [
'conditions' => ["MainMessages.users LIKE" => "%:".$id."%"],
'contain' => ['Users']
]);
Bref j'ai passé un peu de temps dessus surtout que j'ai tuilsié angular pour les requetes et l'affichage des participants et que j'ai du traduire le framework (je voulais utiliser la méthode timeAgoInWords de cakephp3).
Qu'en pensez-vous ?