Bonjour,
j'ai des Documents et des Tags. Mes Documents peuvent avoir plusieurs Tags.
J'aimerai avoir la liste des Documents qui ont les Tags X, Y et Z.
Je n'arrive pas à trouver une solution.

Ce que j'ai :

  • Model Documents :

    public $hasAndBelongsToMany = array('Tag');
    public $hasMany = array('TagR');

  • Model Tags :

    public $hasAndBelongsToMany = array('Document');

  • Model TagR :

    public $useTable = 'documents_tags';
    public $belongsTo = array('Document', 'Tag');

Les bases de données sont en place avec des données à l'intérieur.
une fonction search() dans DocumentsController et un fichier search.ctp qui me donne un form avec une liste de checkbox correspondant à tous les tags dispo.

Quand je lance une recherche, $this->request->data contient :

array(
    'Documents' => array(
        'tags' => array(
            (int) 0 => '12',
            (int) 1 => '16',
            (int) 2 => '19'
        )
    )
)

J'ai donc essayé d'atteindre les Documents dont les Tags.id sont au moins ceux du dessus (dans l'exemple 12, 16 et 19).

$resultats = $this->Document->find('all', array(
                'fields' => array('Document.name'),
                'contain' => array(
                    'Tag' => array(
                        'conditions' => array(
                            'AND' => array('Tag.id' => 12, 'Tag.id' => 16, 'Tag.id' => 19)
                        ),
                        'fields' => array('Tag.name'),
                    )
                )
            ));

Sauf que ca me renvoi tous les Documents, avec un array 'Tag' vide pour ceux qui ne correspondent pas aux critères de recherche. Après je dois recomposer un nouveau tableau en éliminant les entrées donc le array 'Tag' et vide et ne garder que les autres.

Mon problème :

  • A mon avis, ce n'est pas la bonne façon de faire et il existe un moyen pour ne me retourner QUE les documents qui ont été tagé au minimum avec les 3 tags de la recherche.

Merci d'avance.

EDIT : Non en fait même ca, ca ne fonctionne pas.

2 réponses


Fait une requête dans l'autre sens ! Recherche d'abord les tags et récupère les documents associés

$this->Tag->find('all', array(
    'fields' => array('Tag.name'),
    'conditions' => array('Tag.name' => array('X', 'Y', 'Z')),
    'contains' => array(
        'Document' => array(
            'fields' => 'Document.name'
        )
    )
));

En effet c'est peut etre mieux en attaquant par les Tags

$resultats = $this->Document->Tag->find('all', array(
                'fields' => array('Tag.name'),
                'conditions' => array('Tag.id' => $this->request->data'Documents']'tags']),
                'contain' => array(
                    'Document' => array(
                        'fields' => array('Document.id', 'Document.name')
                    )
                )
            ));

ce qui me donne ca en résultat, quand je fais une recherche avec un couple de tags :

array(
    (int) 0 => array(
        'Tag' => array(
            'name' => 'Nom du tag 10',
            'id' => '10'
        ),
        'Document' => array(
            (int) 0 => array(
                'id' => '23',
                'name' => 'Nom du doc 23',
                'DocumentsTag' => array(
                    'id' => '42',
                    'tag_id' => '10',
                    'document_id' => '23'
                )
            ),
            (int) 1 => array(
                'id' => '21',
                'name' => 'Nom du doc 21',
                'DocumentsTag' => array(
                    'id' => '34',
                    'tag_id' => '10',
                    'document_id' => '21'
                )
            ),
            (int) 2 => array(
                'id' => '20',
                'name' => 'Nom du doc 20',
                'DocumentsTag' => array(
                    'id' => '30',
                    'tag_id' => '10',
                    'document_id' => '20'
                )
            )
        )
    ),
    (int) 1 => array(
        'Tag' => array(
            'name' => 'Nom du tag 20',
            'id' => '20'
        ),
        'Document' => array(
            (int) 0 => array(
                'id' => '23',
                'name' => 'Nom du doc 23',
                'DocumentsTag' => array(
                    'id' => '59',
                    'tag_id' => '20',
                    'document_id' => '23'
                )
            ),
            (int) 1 => array(
                'id' => '25',
                'name' => 'Nom du doc 25',
                'DocumentsTag' => array(
                    'id' => '51',
                    'tag_id' => '20',
                    'document_id' => '25'
                )
            ),
            (int) 2 => array(
                'id' => '20',
                'name' => 'Nom du doc 20',
                'DocumentsTag' => array(
                    'id' => '33',
                    'tag_id' => '20',
                    'document_id' => '20'
                )
            )
        )
    )
)

Pour avoir le vrai résultat, il me reste plus qu'a intersecter les tableaux de chaque tag pour trouver les documents communs, à savoir le 23 et le 20. Sauf que array_intersect() ne prend en charge que la comparaison de deux tableaux, alors que je pourrais en avoir bien plus, suivant le nombre de tags de ma recherche et il ne fonctionne pas avec les tableaux multi-dimensionnels...

J'ai fait des recherches sur le net, sans trouver de solution qui fonctionne. J'en appelle une nouvelle fois à vous, on avance, on y est presque ^^ . Je continue de chercher de mon coté, mais si vous avez des idées je prends.

La prochaine étape ca sera de faire une recherche avec des opérateurs : je veux trouver les documents qui ont les Tags 20 et 23 mais qui n'ont pas les tag 21 et 24 par exemple. Mais ca on verra après :)
J'en revient pas de trouver si peu d'exemple sur le net de recherche par plus d'un tag avec cakephp...