Bonjour a tous !

J'aimerai afficher sur mon carrousel en page d'accueil les trois dernières activités de mon site.
Pour ça j'aimerai trouver dans ma bdd l'ensemble de ces activités. Plus précisément l'id, le titre, le slug.. le tout avec un champ qui précise dans quelle bdd l'activité provient.

Vous l'aurez deviné, ces activités sont réparties dans différentes pages du site, et donc dans différentes tables de la bdd.
La page d'accueil (je précise) n'a pas de bdd en rapport avec cette requête.

Pour le moment j'ai ca :

$show = $this->Page->find('all', array('joins' => array(
            array(
                'table' => 'actus',
            ),
            array(
                'table' => 'books',
            ),
            array(
                'table' => 'posts',
            ),
            array(
                'table' => 'stores',
            ),
            array(
                'table' => 'videos',
            )
        )));

..comment dire.. je sens que je suis loin d'un résultat très correct ^^

Je précise que je débute en cakephp, et que sur le site de cakephp je ne sais pas trop par où commencer... :/

Merci de votre précieuse aide !! :)

5 réponses


Pakito
Réponse acceptée

A vrai dire il y a plusieurs manières de faire, et il n'y a pas de "meilleure solution", tout dépend en fait de l'architecture globale de ton application.

La première, c'est de faire une boucle et de charger les données de plusieurs modèles "proprement".
L'avantage, c'est que cela te donne un libre accès à la méthode Model::find(), et donc d'utiliser des conditions, des contain etc, comme tu le ferais pour n'importe quel find.
L'inconvénient c'est que ça peut être lourd, puisque pour chaque model, tu auras au moins une requête qui partira, peut être plus si tu as des contain. Et avec cette méthode, tu ne récupères pas les 3 dernières activités de ton site, tu récupères simplement les n derniers enregistrements de chaque model, et c'est à toi de voir lesquels afficher.

Le code pourrait ressembler à ça :

$models = array('Actu', 'Book', 'Post', 'Store', 'Video');
$results = array();
foreach($models as $model) {
    $this->loadModel($model);
    $results$model] = $this->{$model}->find('all', array(
        'conditions' => array(
            $model'.pusblished' => true // Ceci est un exemple de condition, pour ne récupérer que les éléments publiés
        ),
        'limit' => 5 // Ainsi tu récupères 5 éléments de chaque model
    ));
}

La seconde méthode, c'est d'utiliser du JOIN ou de l'UNION. D'ailleurs, l'UNION serait préférable puisque ta condition va donc se porter sur le created et sera partagée pour toutes les tables.
L'avantage de cette méthode, c'est qu'il n'y a qu'une requête qui part.
L'inconvénient, c'est que tu dois la construire à la main si tu te tournes vers UNION. Avec Join, tu peux faire ça avec un find, mais ça demande quand même pas mal d'ajustements (comme dans l'exemple ici : http://stackoverflow.com/questions/3536107/union-syntax-in-cakephp).

La troisième méthode n'est valable que si tu as des tables aux architectures relativement similaire, à l'exception de quelques champs spécifiques.
Admettons que pour chacun des models que tu as présenté tu aies une structure du type : id, name, slug, created, updated ; l'idée serait alors de faire un système à la Wordpress ou au polymorphic de Ruby on Rails : une seule table à la structure étendue qui est utilisée par plusieurs models.

Sur Cake, c'est déconseillé parce que mal géré en natif, mais il y a tout de même des manières de faire.
Par exemple, admettons que tu aies une unique table "posts", et un model post. Si demain tu dois étendre cette table à un second model, tu ajoutes une colonne "type", et après tu définis tes valeurs, du genre 0 => Post, 1 => Actu, 2 => Book, etc.
D'ailleurs, tu dois pouvoir gérer cet enum des différents types dans ton AppModel.

Ensuite, c'est un peu d'adaptation dans chacun des models qui hérite de post :

class Book extends AppModel {
   var $useTable = 'posts'; 
   function beforeFind($queryData) {
        // Ici, tu dois surcharger le param conditions des query.
        // Il faut faire des find avec une condition "type => 2", puisqu'on est sur Book.
   }
   function beforeSave() {
       // Ici tu dois surcharger les datas qui sont save pour toujours passer le type à 2.
       // Même principe que pour le find.
   }
}

Voila. Donc tout dépend d'où tu en es dans ton application, de ton expertise en SQL et de la maintenabilité que tu souhaites obtenir.

Canonier
Auteur

Merci pour cette réponse très complète !

Malgré la complexité de l'UNION, je pense que c'est la meilleure solution !
Pour tester le fonctionnement de celui-ci, je peux m'exercer que sur deux tables, ou est-ce n'est pas une fonction commutative ?..

Quoi qu'il en soit, je vais faire des essais, et je reviendrai pour donner ma solution, ou mes futurs blocages ^^

Merci encore ;)

Tu peux tout à fait ne t'exercer que sur deux tables ;)

Canonier
Auteur

C'est vrai que sur le papier faire deux tables serait bien plus simple !
L'info qui justifie mon choix c'est que les tables sont pas exactement identiques..

En gros suivant la catégorie (ou page) le contenu peut-être précédé d'une introduction, et parfois encore d'une description de 300 caractères..
et suivant la catégorie donc, j'affiche dans la liste l'extrait qu'il faut!..

Mais c'est vrai qu'en y reflechissant un peu plus.. j'aurai pu (et je peux encore) prendre la solution a deux tables..
Je pense que le fait de commencer sur cakephp, je me suis aussi laissé guider par la facilité de devoir crée la table pour le model avec la vue.. et patati et patata..

Je vais certainement changer tout ca puisque je commence a me perdre dans mes UNIONs..
le code qu'il faut que je mette dans les models c'est bien..? :

public $useTable = 'Table Post';

Mais pour la page d'accueil il faut bien que je crée un fichier Page.php dans Model, ou je dois simplement éditer le fichier AppModel.php ? ^^

EDIT : je l'ai placé dans le AppModel.. et du coup je viens de comprendre à quoi sert ce fichier x)
Comme son nom l'indique finalement ^^

Canonier
Auteur

Apres tous vos beaux conseils, me voila avec deux tables :)
A partir de là j'ai en effet réussi à afficher les trois dernières activité sans soucis. Je vous remercie !

Je précise que j'ai quand meme utilisé le code dans AppModel, pour ne pas avoir à le copier partout !