Jointure sql sous yiiframework !

Bonjour à tous,

n’étant pas du tout bilingue je requière votre aide pour un projet !

Alors voila j’aimerais savoir comment faire des jointures sql sous yii.

Par exemple j’ai quatre tables :

catégorie :

id - nom_catégorie

sous_catégorie :

id - id_catégorie - nom_sous_catégorie

membres :

id - pseudo - pass - email

tutoriel :

id - id_membres - id_catégorie - id_sous_catégorie - contenu - titre

Vous l’aurez compris lorsque j’affiche un tutoriel j’aimerais pouvoir afficher le titre, le contenu, le pseudo du membre, la catégorie, la sous catégorie !

Malheureusement je n’ai pas vraiment saisi comment réaliser une jointure sql sous ce framework ! (J’avoue que mes habitues sont perturbé sous ce framework il l’on ne dois écrire aucune requête sql).

Merci de votre aide !

Salut Co-k-ine,

pour faire des jointures, c’est fastoche. Yii propose une classe CActiveRecord qui, une fois dérivée, permet de tout faire (ou presque) sans avoir à taper une seule ligne de SQL (ou juste des petits bouts).

Pour reprendre ton exemple, la première chose à faire est de créer une classe modèle par table. Pour cela, le plus simple c’est de laisser Yii se charger du travail. En effet, le programme yiic en ligne de commande permet de générer automatiquement des modèles pour les tables de ta base de données (il fait beaucoup plus, mais bon, restons dans le sujet).

Si tu utilises la dernière version de Yii (la 1.2) c’est encore plus simple ! pas la peine de ligne de commande, le module gii fait ça tout seul (ou presque).

Disons que tes classes de modèles sont crées. Tu en as 4 : Categorie.php, sousCategorie.php, membres.php et enfin tutoriel.php.

Et là je dis attention ! Si tes tables n’ont pas de clefs primaires composites, et si les colonnes id_catégorie, id_membres et id_sous_catégorie sont correctement déclarées comme des clefs étrangères, c’est magique, Yii a créé tout seul les relations entres les tables. C’est pas super ça ? … si tu veux voir ça d’un peu plus prés, regarde la classe Categorie. Elle contient une méthode qui s’appelle relations et qui renvoie un tableau dans lequel sont déclarées toutes les relations (jointures) qui ont été détectées par Yii.

Maintenant comment exploiter tout ça ? comment ‘faire’ la jointure ? …

Il suffit d’écrire :




// récupère toutes les catégories avec pour chacun d'elles, tous les tutoriaux

$categories=Categorie::model()->with('tutoriel')->findAll();

foreach($categories as $categorie) {


      echo 'id = '.$categorie->id.'<br/>';

      $tutoriaux=$categorie->tutoriel;

      foreach($categorie->tutoriel as $unTutoriel){

          echo 'tutoriel = '.$unTutoriel->titre;

      }

}



Voilà.

C’est un résumé, mais j’espère que cela t’auras permis d’y voir plus clair. Je te conseille de bien regarder la classe CActiveRecord qui permet beaucoup plus que ça… Par contre si tu ne parles, ni ne lis l’anglais, ça risque d’être un peu plus compliqué , la communauté française n’étant pas des plus active. Il y a le site http://forum.yiiframework.fr/ mais aujourd’hui je vois qu’il ne répond plus.

Bon courage

B)

Merci beaucoup pour ta réponse rapide Raoul !

J’avais créer à l’aide de yiic un model/Crud pour chaqu’une des tables ! Et je cherchais justement à faire des relations entres elles !

J’essayerais de faire cela avec gii sais-tu si les commandes sont les mêmes ? (Model tutoriel – CRUD tutoriel)

En tout cas je regarderais cela de plus prêt ce week end. J’espère ne pas trop galérer !

PS : N’aurais tu pas un tutoriel sous la main pour être certain de bien magner mes tables sql avec gii ?

Merci encore pour ton aide

Salut Co-k-ine,

si tu as déjà créé un CRUD avec yiic, tu peux voir qu’il a bien créé les relations entre les tables … enfin, si tes tables avaient de relations. N’oublie pas que yiic (et gii) ne savent pas gérer des tables avec des clefs primaires composites (multiples). C’est triste, mais pour l’instant c’est une limitation.

Si tu veux utiliser Gii, tu dois avoir la dernière version de Yii. Ensuite, tu dois déclarer le module Gii dans la configuration de ton application (protected/config/main.php) en insérant le code suivant :




return array(

    ......

    'modules'=>array(

        // declaration du module gii

        'gii'=>array(

            'class'=>'system.gii.GiiModule',

            'password'=>'tonMotDePasseIci',

            // 'ipFilters'=>array(...a list of IPs...),

            // 'newFileMode'=>0666,

            // 'newDirMode'=>0777,

        ),

    ),

);



Enfin, tu accèdes à l’adresse http://hostname/path/to/index.php?r=gii et miracle ! l’interface de Gii apparaît. Tu peux faire avec Gii tout ce que tu pouvais faire avec yiic (et même un peu plus je crois), sans avoir à taper de commande, juste des clicks et des formulaires.

Concernant le tutoriel (en français j’imagine ;) ) désolé, mais je n’ai pas ça sous la main. La communauté Yii est fortement anglophone, et il est dommage que le site http://www.yiiframework.fr ne réponde plus… enfin pour l’instant.

ciao

B)

Bonsoir,

Je tiens encore à vous remercier pour votre réponse rapide ! Malheureusement j’ai l’impression que je vais encore avoir besoin de votre aide ! Je suis navré de vous faire perdre votre temps.

J’ai donc modifier un peut le nom de mes tables, voici ma structure sql :


-- phpMyAdmin SQL Dump

-- version 3.3.3

-- http://www.phpmyadmin.net

--

-- Serveur: localhost

-- Généré le : Ven 11 Juin 2010 à 20:51

-- Version du serveur: 5.1.41

-- Version de PHP: 5.3.2-1ubuntu4.2


SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";




/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;

/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;

/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;

/*!40101 SET NAMES utf8 */;


--

-- Base de données: `yii`

--


-- --------------------------------------------------------


--

-- Structure de la table `discipline`

--


CREATE TABLE IF NOT EXISTS `discipline` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `nom` varchar(150) NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;


--

-- Contenu de la table `discipline`

--


INSERT INTO `discipline` (`id`, `nom`) VALUES

(1, 'informatique');


-- --------------------------------------------------------


--

-- Structure de la table `sous_discipline`

--


CREATE TABLE IF NOT EXISTS `sous_discipline` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `id_discipline` int(11) NOT NULL,

  `nom_sous_discipline` varchar(150) NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;


--

-- Contenu de la table `sous_discipline`

--


INSERT INTO `sous_discipline` (`id`, `id_discipline`, `nom_sous_discipline`) VALUES

(1, 1, 'yiiframework');


-- --------------------------------------------------------


--

-- Structure de la table `tutoriel`

--


CREATE TABLE IF NOT EXISTS `tutoriel` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `sous_discipline_id` int(11) NOT NULL,

  `titre` varchar(150) NOT NULL,

  `contenu` text NOT NULL,

  `valide` int(11) NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;


--

-- Contenu de la table `tutoriel`

--


INSERT INTO `tutoriel` (`id`, `sous_discipline_id`, `titre`, `contenu`, `valide`) VALUES

(1, 1, 'La base des jointures', 'Ici je teste les jointures', 1);

J’ai donc essayer me familiariser avec l’outil graphique gii en suivant scrupuleusement ce tutoriel : http://www.yiiframework.com/doc/guide/quickstart.first-app

Je tiens à dire que je n’ai malheureusement pas réussit à créer automatiquement les relations entre ces différentes tables!

Peut-être pourriez vous m’expliquez d’où cela pourrais venir ? Auriez vous besoins des modèles générer avec gii ?

PS : j’ai tenté de créer une clé étrangère “manuellement” mais j’obtiens un message d’erreur :


ALTER TABLE sous_discipline ADD FOREIGN KEY ( 'id_discipline' ) REFERENCES DISCIPLINE( 'ID' ) 

Encore merci pour votre aide raoul !

Salut Co-k-ine,

la définition des tables que tu me présentes là, ne contient pas de clef étrangère, et donc d’un point de vue SQL, il n’y a pas de relation entre la table ‘discipline’ et le table ‘sous_discipline’, par conséquent il est normal que yii ne découvre aucune relation.

Là on quitte le sujet ‘Yii Framework’ pour le sujet ‘Architecture des base de données’, et dans ce domaine je suis loin d’être un expert. Alors du coup, la solution que j’ai trouvé, c’est d’utiliser une application qui fait le boulot à ma place ! Notamment pour ce qui concerne le SQL. J’utilise “MySQL Workbench” qui est gratuit et téléchargeable ici. Avec cette application, tu peux dessiner tes tables, les lier entre elles, et tout ça de façon graphique, sans avoir à te soucier du SQL, qui sera généré automatiquement en fonction du schema que tu aura créé.

Tu peux essayer de voir si cela te convient, mais si tu veux aller plus loin, je crains que tu ne puisses faire l’économie de la lecture de quelques documentation sur le sujet (qui va du ‘tout simple’ au ‘bien complexe’).

Revenons à ta question. J’ai donc modélisé tes tables avec MySQL WorkBench (1 minute), puis j’ai demander à récupérer le SQL qui créé ce modèle (2 secondes), et voilà le résultat :




SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;

SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;

SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';




-- -----------------------------------------------------

-- Table `discipline`

-- -----------------------------------------------------

CREATE  TABLE IF NOT EXISTS `discipline` (

  `id` INT NOT NULL AUTO_INCREMENT ,

  `nom` VARCHAR(150) NOT NULL ,

  PRIMARY KEY (`id`) )

ENGINE = InnoDB;




-- -----------------------------------------------------

-- Table `sous_discipline`

-- -----------------------------------------------------

CREATE  TABLE IF NOT EXISTS `sous_discipline` (

  `id` INT NOT NULL AUTO_INCREMENT ,

  `id_discipline` INT NOT NULL ,

  `nom_sous_discipline` VARCHAR(150) NOT NULL ,

  PRIMARY KEY (`id`) ,

  INDEX `fk_sous_discipline_discipline1` (`id_discipline` ASC) ,

  CONSTRAINT `fk_sous_discipline_discipline1`

    FOREIGN KEY (`id_discipline` )

    REFERENCES `discipline` (`id` )

    ON DELETE NO ACTION

    ON UPDATE NO ACTION)

ENGINE = InnoDB;




-- -----------------------------------------------------

-- Table `tutorial`

-- -----------------------------------------------------

CREATE  TABLE IF NOT EXISTS `tutorial` (

  `id` INT NOT NULL AUTO_INCREMENT ,

  `id_sous_discipline` INT NOT NULL ,

  `titre` VARCHAR(150) NOT NULL ,

  `contenu` TEXT NOT NULL ,

  `valide` INT(11) NOT NULL ,

  PRIMARY KEY (`id`) ,

  INDEX `fk_tutorial_sous_discipline1` (`id_sous_discipline` ASC) ,

  CONSTRAINT `fk_tutorial_sous_discipline1`

    FOREIGN KEY (`id_sous_discipline` )

    REFERENCES `sous_discipline` (`id` )

    ON DELETE NO ACTION

    ON UPDATE NO ACTION)

ENGINE = InnoDB;




SET SQL_MODE=@OLD_SQL_MODE;

SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;

SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;




Comme tu le vois, cette génération automatique est bien utile aussi pour ce former au SQL et à ce titre, permet d’apprendre comment créer des relations entre tables.

Bon, la prochaine étape c’est de réellement créer ces tables dans ta base de données de test (sur ton PC). Pour ça, MySQL WorkBench dispose d’une fonction ‘forward enginering’ toute prête (à configurer un peu quand même). En un petit click, c’est fait.

Dernière étape : laisser Yii créer les modèles correspondants. C’est facile avec un module Gii bien configuré. Le résultat est le suivant :

[size="5"]table discipline[/size]




<?php


/**

 * This is the model class for table "discipline".

 */

class Discipline extends CActiveRecord

{

    /**

     * The followings are the available columns in table 'discipline':

     * @var integer $id

     * @var string $nom

     */

    /**

     * Returns the static model of the specified AR class.

     * @return Discipline the static model class

     */

    public static function model($className=__CLASS__)

    {

        return parent::model($className);

    }

    /**

     * @return string the associated database table name

     */

    public function tableName()

    {

        return 'discipline';

    }

    /**

     * @return array validation rules for model attributes.

     */

    public function rules()

    {

        // NOTE: you should only define rules for those attributes that

        // will receive user inputs.

        return array(

            array('nom', 'required'),

            array('nom', 'length', 'max'=>150),

            // The following rule is used by search().

            // Please remove those attributes that should not be searched.

            array('id, nom', 'safe', 'on'=>'search'),

        );

    }

    /**

     * @return array relational rules.

     */

    public function relations()

    {

        // NOTE: you may need to adjust the relation name and the related

        // class name for the relations automatically generated below.

        return array(

            'sousDisciplines' => array(self::HAS_MANY, 'SousDiscipline', 'id_discipline'),

        );

    }

    /**

     * @return array customized attribute labels (name=>label)

     */

    public function attributeLabels()

    {

        return array(

            'id' => 'ID',

            'nom' => 'Nom',

        );

    }

    /**

     * Retrieves a list of models based on the current search/filter conditions.

     * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.

     */

    public function search()

    {

        // Warning: Please modify the following code to remove attributes that

        // should not be searched.


        $criteria=new CDbCriteria;

        $criteria->compare('id',$this->id);

        $criteria->compare('nom',$this->nom,true);

        return new CActiveDataProvider(get_class($this), array(

            'criteria'=>$criteria,

        ));

    }

} 



[size="5"]table sous_discipline[/size]




<?php


/**

 * This is the model class for table "sous_discipline".

 */

class SousDiscipline extends CActiveRecord

{

    /**

     * The followings are the available columns in table 'sous_discipline':

     * @var integer $id

     * @var integer $id_discipline

     * @var string $nom_sous_discipline

     */


    /**

     * Returns the static model of the specified AR class.

     * @return SousDiscipline the static model class

     */

    public static function model($className=__CLASS__)

    {

        return parent::model($className);

    }


    /**

     * @return string the associated database table name

     */

    public function tableName()

    {

        return 'sous_discipline';

    }


    /**

     * @return array validation rules for model attributes.

     */

    public function rules()

    {

        // NOTE: you should only define rules for those attributes that

        // will receive user inputs.

        return array(

            array('id_discipline, nom_sous_discipline', 'required'),

            array('id_discipline', 'numerical', 'integerOnly'=>true),

            array('nom_sous_discipline', 'length', 'max'=>150),

            // The following rule is used by search().

            // Please remove those attributes that should not be searched.

            array('id, id_discipline, nom_sous_discipline', 'safe', 'on'=>'search'),

        );

    }


    /**

     * @return array relational rules.

     */

    public function relations()

    {

        // NOTE: you may need to adjust the relation name and the related

        // class name for the relations automatically generated below.

        return array(

            'idDiscipline0' => array(self::BELONGS_TO, 'Discipline', 'id_discipline'),

            'tutorials' => array(self::HAS_MANY, 'Tutorial', 'id_sous_discipline'),

        );

    }


    /**

     * @return array customized attribute labels (name=>label)

     */

    public function attributeLabels()

    {

        return array(

            'id' => 'ID',

            'id_discipline' => 'Id Discipline',

            'nom_sous_discipline' => 'Nom Sous Discipline',

        );

    }


    /**

     * Retrieves a list of models based on the current search/filter conditions.

     * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.

     */

    public function search()

    {

        // Warning: Please modify the following code to remove attributes that

        // should not be searched.

        $criteria=new CDbCriteria;

        $criteria->compare('id',$this->id);

        $criteria->compare('id_discipline',$this->id_discipline);

        $criteria->compare('nom_sous_discipline',$this->nom_sous_discipline,true);

        return new CActiveDataProvider(get_class($this), array(

            'criteria'=>$criteria,

        ));

    }

} 



[size="5"]table tutorial[/size]




<?php


/**

 * This is the model class for table "tutorial".

 */

class Tutorial extends CActiveRecord

{

    /**

     * The followings are the available columns in table 'tutorial':

     * @var integer $id

     * @var integer $id_sous_discipline

     * @var string $titre

     * @var string $contenu

     * @var integer $valide

     */


    /**

     * Returns the static model of the specified AR class.

     * @return Tutorial the static model class

     */

    public static function model($className=__CLASS__)

    {

        return parent::model($className);

    }


    /**

     * @return string the associated database table name

     */

    public function tableName()

    {

        return 'tutorial';

    }


    /**

     * @return array validation rules for model attributes.

     */

    public function rules()

    {

        // NOTE: you should only define rules for those attributes that

        // will receive user inputs.

        return array(

            array('id_sous_discipline, titre, contenu, valide', 'required'),

            array('id_sous_discipline, valide', 'numerical', 'integerOnly'=>true),

            array('titre', 'length', 'max'=>150),

            // The following rule is used by search().

            // Please remove those attributes that should not be searched.

            array('id, id_sous_discipline, titre, contenu, valide', 'safe', 'on'=>'search'),

        );

    }

    /**

     * @return array relational rules.

     */

    public function relations()

    {

        // NOTE: you may need to adjust the relation name and the related

        // class name for the relations automatically generated below.

        return array(

            'idSousDiscipline0' => array(self::BELONGS_TO, 'SousDiscipline', 'id_sous_discipline'),

        );

    }

    /**

     * @return array customized attribute labels (name=>label)

     */

    public function attributeLabels()

    {

        return array(

            'id' => 'ID',

            'id_sous_discipline' => 'Id Sous Discipline',

            'titre' => 'Titre',

            'contenu' => 'Contenu',

            'valide' => 'Valide',

        );

    }


    /**

     * Retrieves a list of models based on the current search/filter conditions.

     * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.

     */

    public function search()

    {

        // Warning: Please modify the following code to remove attributes that

        // should not be searched.


        $criteria=new CDbCriteria;

        $criteria->compare('id',$this->id);

        $criteria->compare('id_sous_discipline',$this->id_sous_discipline);

        $criteria->compare('titre',$this->titre,true);

        $criteria->compare('contenu',$this->contenu,true);

        $criteria->compare('valide',$this->valide);

        return new CActiveDataProvider(get_class($this), array(

            'criteria'=>$criteria,

        ));

    }

} 



Comme tu peux le constater, les tables ‘sous_discipline’ et ‘tutorial’ contiennent toutes les deux cette fameuse méthode relations qui défini les relations existantes entre les tables.

Pour comprendre leur signification, tu dois te référer à la documentation de Yii, mais la lecture seule du code est déjà claire :

  • un tutoriel appartient (BELONGS_TO) une sous-discipline

  • une sous-discipline a plusieurs (HAS_MANY) tutoriels, et appartient (BELONGS_TO) a une discipline

  • une discipline a plusieurs (HAS_MANY) sous-discipline

La définition d’un schéma de base de données est extrêmement importante, car c’est le socle sur lequel s’appuiera ton application, donc il faut bien y réfléchir avant de commencer tout développement. C’est sur ce point que la lecture de documentation sur le sujet s’avère payante (surtout le jour ou tu voudra rajouter des tables …). Bref, c’est un vaste sujet, et comme je l’ai dit, je suis loin d’avoir suffisamment de connaissance pour te donner un autre conseil que celui de te documenter ;)

J’espère que tout cela t’aura aidé, et bon courage pour la suite …

ciao

8)

Merci Raoul pour cette explication très éclairante :)