Je vais essaye d'être rapide dans les explications :
j'ai une requête assez spéciale à réaliser à cause de la structure des données : une table Users, et une table Friends avec 2 clés étrangères (ami_from, et ami_to) toute 2 clés primaire de Users. C'est un systeme classique de "je suis fan de" et "je suis suivis par"...
La requète permet de retrouver les amis, c'est à dire les utilisateurs qui se suivent mutuellement.
et la requète fonctionne bien :
<?php
$query = $this->Friends->find();
$query->select([
'countid' => $query->func()->count('*'), 'Users.username','Users.id','Users.avatar','Users.naiss','Users.created_at'
])
->join([
'table' => 'Users',
//'alias' => 'u',
'type' => 'LEFT',
'conditions' => 'Users.id = (Friends.ami_from + Friends.ami_to - '.$id.')',
])
->where([
'OR' => [['Friends.ami_from' => $id], ['Friends.ami_to' => $id]],
])
->group('Users.username')
->having(['countid >' => 1]);
$this->paginate = [
'limit'=>20
];
$this->set('friends', $this->paginate($query));
?>
Les données renvoyées sont parfaitement cohérentes. Mon problème vient de l'object retourné par cakephp. Il ne charge pas l'entity de Users, mais retourne juste les données sous forme de tableau.Du coup Je ne peux pas exploiter les valeurs retournées qui utilisent des formatages dans l'entity Users.
J'imagine que ça vient du ->join effectué manuellement plutôt qu'un ->contain, mais je ne trouve pas d'autres moyens pour écrire cette requète. Je n'utilise pas contain car je n'arrive pas à cause de la structure en double clé primaire de Friends elles même clé primaire de la même table Users.
Pour vous donner une idée, la requète à construire est :
SELECT (COUNT(*)) AS `countid`, Users.username , Users.id , Users.avatar , Users.created_at, Friends.ami_from AS `Friends__ami_from`, Friends.ami_to AS `Friends__ami_to`, Friends.created_at AS `Friends__created_at` FROM Friends LEFT JOIN Users ON Users.id = (Friends.ami_from + Friends.ami_to - $id) WHERE (Friends.ami_from = $id OR Friends.ami_to = $id) GROUP BY Users.username HAVING countid > '1'
YO. Est-ce qu'on peut voir ce fameux tableau (via un pr())? Modifie les données sensibles, si besoin :-)
Sinon pour ton système, avec une association de type HABTM tu peux utiliser le contain
, non? ( voir http://book.cakephp.org/3.0/fr/orm/associations.html#associations-belongstomany )
non avec un HABTM j'aurai quelque chose du genre $this->Users->find()->contain(['Users'])
et ça devient vraiment bordelique à manier.
Moi j'aurai surtout voulu garder cette requète et faire comprendre à cakephp que la table dans le ->join doit etre chargé avec l'entity correspondante, ce qu'il ne fait pas et j'ai du mal à comprendre pourquoi.
Bordelique? :-O
Avec seulement ceci dans le controlleur (+ 3 associations habtm dans UsersTable
)
$users = $this
->Users
->find()
->select(['id', 'username'])
->contain([
'Followings',
'Followers',
'Friends' => function ($query) {
return $query->where(function ($query) {
return $query->like('Relationships.status', '%Friend request accepted.%');
});
},
])
;
J'ai ce résultat:
Ceci étant dit, je n'ai jamais eu a utiliser le join() avec cakephp mais dans la logique, les données qui te sont retournées sont bels & biens correctes. Maintenant pourquoi ils ne convertissent pas automatiquement le tableau vers l'entité, je ne sais pas.
Tu peux leur demander \ö/ mais il y a peut-être une autre façon de faire. ^^
Je veux bien voir tes associations dans usertable, ton système à l'air mieux foutu que le mien. merci de ton aide dans tout les cas.
Yo. Bien évidemment... :-)
Dans la table UsersTable
:
public function initialize(array $config)
{
// ...
$habtm_options = [
'className' => 'Users',
'joinTable' => 'relationships', # chez toi `friends`
];
$followings = $followers = $friends = $habtm_options;
$followings += [
'foreignKey' => 'ref_id', # from
'targetForeignKey' => 'relationship_id', # to
];
$followers = $friends += [
'foreignKey' => 'relationship_id', # to
'targetForeignKey' => 'ref_id', # from
];
$friends += ['conditions' => ['Relationships.status LIKE' => '%Friend request accepted.%']];
# Les abonnements ont, et appartiennent à, plusieurs utilisateurs.
$this->belongsToMany('Followings', $followings);
# Les abonnés ont, et appartiennent à, plusieurs utilisateurs.
$this->belongsToMany('Followers', $followers);
# Les amis ont, et appartiennent à, plusieurs amis.
$this->belongsToMany('Friends', $friends);
// ...
}
Dans une méthode d'un controlleur:
# Contient les données des champs suivants:
# - {id, username, :virtual_prop} (->select())
# - friends {id, username, password, etc... (à limiter), _joinData, :virtual_prop}
# - followers {id, username, password, etc... (à limiter), _joinData, :virtual_prop}
# - followings {id, username, password, etc... (à limiter), _joinData, :virtual_prop}
$users = $this
->Users
->find()
->select(['id', 'username'])
->contain([
'Followings',
'Followers',
'Friends',
])
;
pr(json_encode($users, JSON_PRETTY_PRINT));
die();
Pour expliquer un peu mes tables en bdd fait à la va vite...
Je n'ai que deux champs dans ma table users
(pour le test.):
users
.id
users
.username
Et quatre champs nécessaires (pour le systeme que j'ai fait) pour la table relationships
:
relationships
.id
relationships
.ref_id
(from
)relationships
.relationship_id
(to
)relationships
.status
(Nouveau champ à ajouter…)Voilà…
D'accord ton système est de type "facebook" j'ai l'impression. avec des demandes d'amitiés et des confirmations. Je n'ai pas vraiment la même logique mais tu m'as quand même bien aiguillé merci.
(Toute dernière question hors sujet : pourquoi crypter les identifiants de tes utilisateurs ?)
C'est vrai que j'avais cette logique là, mais ce ne sont juste des messages pour dire qu'ils se sont suivis.
Faut adapter le contenu:
"User1
et User2
se suivent mutuellement." ou autre chose, à la place de "Friend request accepted." (LIKE %se suivent mutuellement.%
)
"User3
a ajouté User1
à ses abonnements." ou autre chose, à la place de "Friend request sent." ^^
Pour les clés primaires, c'était/c'est dans leurs conventions, j'aime bien les conventions. Du coup j'ai gardé cette habitude de mettre que des uuid
en clé primaire pour Cake. (CakePHP les génère automatiquement lors d'un save().)
C'est un choix personnel après je pense mais moi je m'y retrouve mieux, tu peux très bien utiliser des clés auto-incrémentées.
Hormis ça, rien d'autre x)