Retour PDO

Eléphant du PHP | 451 Messages

10 déc. 2011, 12:28

Bonjour,

Alors voilà j'ai développé mon propre MVC et j'ai un problème sur mes models avec le retour PDO.

Quand je fais un left join j'aimerais bien que PDO le retourne un tableau de ce genre:
object(stdClass) {
     ["Recette"] => object(stdClass) {
          ["id"] => "4"
          ["img"] => "monimage.jpg"
          ["user_id"] => "1"
     }
     ["User"] => object(stdClass) {
          ["id"] => "1"
          ["level"] =>  "1"
     }
     ["Group"] => object(stdClass) {
          ["id"] => "1"
          ["rang"] => "admin"
          ["color"] => "ff0000"
     }
     ["Comment"] => object(stdClass) {
            [0] => object(stdClass) {
                  ["id"] => "1"
                  ["recette_id"] => "5"
                  ["user_id"] => "1"
                  ["message"] => "test"
                  ["created"] => "2011-12-10 10:26:53"
            }
            [1] => object(stdClass) {
                  ["id"] => "2"
                  ["recette_id"] => "5"
                  ["user_id"] => "1"
                  ["message"] => "test"
                  ["created"] => "2011-12-10 10:26:53"
            }
            [2] => object(stdClass) {
                  ["id"] => "3"
                  ["recette_id"] => "5"
                  ["user_id"] => "1"
                  ["message"] => "test"
                  ["created"] => "2011-12-10 10:26:53"
            }
     }
}
Car pour le moment j'ai un retour comme ceci:
array(2) {
  [0]=>
  object(stdClass)#10 (34) {
    ["id"]=>
    string(1) "4"
    ["title"]=>
    string(4) "test"
    ["photos"]=>
    string(0) ""
    ["ingred"]=>
    string(14) "50g de beurre "
    ["etape"]=>
    string(4) "test"
    ["astuce"]=>
    string(0) ""
    ["vin"]=>
    string(0) ""
    ["online"]=>
    string(1) "1"
    ["slug"]=>
    NULL
    ["category_id"]=>
    string(1) "2"
    ["user_id"]=>
    string(1) "1"
    ["created_at"]=>
    string(19) "2011-12-05 22:37:55"
    ["modified_at"]=>
    NULL
    ["username"]=>
    string(5) "admin"
    ["password"]=>
    string(40) "d033e22ae348aeb5660fc2140aec35850c4da997"
    ["mail"]=>
    string(14) "[email protected]"
    ["firstname"]=>
    string(0) ""
    ["lastname"]=>
    string(0) ""
    ["born"]=>
    string(0) ""
    ["sexe"]=>
    string(0) ""
    ["website"]=>
    string(0) ""
    ["pays"]=>
    string(0) ""
    ["ville"]=>
    string(0) ""
    ["bio"]=>
    string(0) ""
    ["level"]=>
    string(1) "1"
    ["active"]=>
    string(1) "1"
    ["ban"]=>
    string(1) "0"
    ["delete"]=>
    string(1) "0"
    ["name"]=>
    string(14) "Administrateur"
    ["color"]=>
    string(6) "dc143c"
    ["order"]=>
    string(2) "10"
    ["recette_id"]=>
    string(1) "5"
    ["message"]=>
    string(4) "test"
    ["created"]=>
    string(19) "2011-12-10 10:26:53"
  }
  [1]=>
  object(stdClass)#11 (34) {
    ["id"]=>
    string(1) "5"
    ["title"]=>
    string(4) "test"
    ["photos"]=>
    string(0) ""
    ["ingred"]=>
    string(14) "50g de beurre "
    ["etape"]=>
    string(4) "test"
    ["astuce"]=>
    string(0) ""
    ["vin"]=>
    string(0) ""
    ["online"]=>
    string(1) "1"
    ["slug"]=>
    NULL
    ["category_id"]=>
    string(1) "2"
    ["user_id"]=>
    string(1) "1"
    ["created_at"]=>
    string(19) "2011-12-05 22:37:55"
    ["modified_at"]=>
    NULL
    ["username"]=>
    string(5) "admin"
    ["password"]=>
    string(40) "d033e22ae348aeb5660fc2140aec35850c4da997"
    ["mail"]=>
    string(14) "[email protected]"
    ["firstname"]=>
    string(0) ""
    ["lastname"]=>
    string(0) ""
    ["born"]=>
    string(0) ""
    ["sexe"]=>
    string(0) ""
    ["website"]=>
    string(0) ""
    ["pays"]=>
    string(0) ""
    ["ville"]=>
    string(0) ""
    ["bio"]=>
    string(0) ""
    ["level"]=>
    string(1) "1"
    ["active"]=>
    string(1) "1"
    ["ban"]=>
    string(1) "0"
    ["delete"]=>
    string(1) "0"
    ["name"]=>
    string(14) "Administrateur"
    ["color"]=>
    string(6) "dc143c"
    ["order"]=>
    string(2) "10"
    ["recette_id"]=>
    string(1) "5"
    ["message"]=>
    string(4) "test"
    ["created"]=>
    string(19) "2011-12-10 10:27:58"
  }
}
Vous voyez c'est bordel et à chaque commentaire il me renvoit un tableau contenant tout.

Donc si vous savez comment je pourrais faire pour obtenir comme le premier tableau.

Voici comment je fais une requête vers mon model:
        $r = $this->Recettes->find(array(
            'conditions' => array('Recettes.id' => $id),
            'joins' => array(
                array(
                    'table' => 'users',
                    'type' => 'left',
                    'conditions' => 'Recettes.user_id = Users.id'
                ),
                array(
                    'table' => 'groups',
                    'type' => 'left',
                    'conditions' => 'Users.level = Groups.id'
                ),
                array(
                    'table' => 'recettescomments',
                    'type' => 'left',
                    'conditions' => 'Recettes.id = Recettescomments.recette_id'
                )
             )
        ));
Et voici ma fonction find():
    public function find($d = null) {
        $valid = true;

        if($this->request->data) {
            $valid = $this->validations($this->request->data);
        }

        if($valid) {
            $sql = 'SELECT ';
            
            if(isset($d['fields'])) {
                if(isset($d['join'])) {
                    
                }
                
                if(!is_array($d['fields'])) {
                    $sql .= $d['fields'];
                } else {
                    $sql .= implode(', ', $d['fields']);
                }
            } else {
                $sql .= '*';
            }
            
            $sql .= ' FROM '.$this->table.' as '.get_class($this);
            
            if(isset($d['joins'])) {
                for($i = 0; $i < count($d['joins']); $i++) {
                    $sql .= ' '.strtoupper($d['joins'][$i]['type']).' JOIN '.strtolower($d['joins'][$i]['table']).' '.ucfirst($d['joins'][$i]['table']);
                    $sql .= ' ON '.$d['joins'][$i]['conditions'];
                }
            }
            
            if(isset($d['conditions'])) {
                $sql .= ' WHERE ';
                
                if(!is_array($d['conditions'])) {
                    $sql .= $d['conditions'];
                } else {
                    $cond = array();
                    
                    foreach ($d['conditions'] as $k => $v) {
                        //if(!is_numeric($v)) {
                            $v = '"'.mysql_escape_string($v).'"';
                        //}
                        
                        $cond[] = "$k = $v";
                    }
                    
                    $sql .= implode(' AND ', $cond);
                }
            }
            
            if(isset($d['limit'])) {
                $sql .= ' LIMIT '.$d['limit'];
            }
            
            if(isset($d['order_by'])) {
                $sql .= ' ORDER BY '.$d['order_by'];
            }

            $pre = $this->db->prepare($sql);
            $pre->execute();

            return $pre->fetchAll(PDO::FETCH_OBJ);
        } else {
            return false;
        }
    }
Merci d'avance......

ViPHP
xTG
ViPHP | 7331 Messages

10 déc. 2011, 12:36

Je ne saurais t'aider à moins de faire le traitement manuellement, mais peut être qu'il y a une solution avec fetchObject.
Par contre...
$v = '"'.mysql_escape_string($v).'"';
:shock:
$v = $this->db->quote($v);
On utilise PDO ou on l'utilise pas, mais faut pas mélanger les oignons avec les pommes ça fait désordre. ;)

Et aussi... Utilises query() au lieu de prepare().
Cela n'a aucun intérêt dans ton cas de faire un prepare().

Eléphant du PHP | 451 Messages

10 déc. 2011, 12:42

Ok mais tu vois j'ai crée mon MVC en regardant des tuto et il disant pas de faire comme tu as dit mais j'ai modifié ce que tu m'as dit :D

Merci xTG et je vais attendre si d'autre on une solution ou une piste pour le retour PDO.

ViPHP
xTG
ViPHP | 7331 Messages

10 déc. 2011, 14:28

PDO::fetchObject() a pas l'air de faire de création d'objet en profondeur.
Le mieux serait donc d'instancier toi même tes objets grâce aux constructeurs.
Donc tu te créés une class Recette, une class User, une Group et une Comment et tu les remplies.

Exemple (basé sur fetch()) :
Un utilisateur a plusieurs nombres. L'exemple est bateau et le code n'est pas forcement joli et optimisé mais le concept est là.
class Utilisateur{
  private static $utilisateurs = array();
  public $id;
  public $nombre = array();
  function __construct($id){
    $this->id = $id;
  }
  public static getUtilisateur( $id ){
    if( !(isSet(self::$utilisateurs[$id]) )
      return new Utilisateur($id);
    return self::$utilisateurs[$id];
  }
}
class Nombre{
  private $nombre;
  function __construct($nombre){
    $this->nombre = $nombre;
  }
}
$retourPDO = array(
  0 => array(
    'nombre' => 1,
    'idUtilisateur' => 1
  ),
  1 => array(
    'nombre' => 1,
    'idUtilisateur' => 2,
  ),
  2 => array(
    'nombre' => 2,
    'idUtilisateur' => 2
  )
);
foreach($retourPDO => $data){
  $utilisateur = Utilisateur::getUtilisateur($data['idUtilisateur']);
  $utilisateur->nombre[] = new Nombre($data['nombre']);
}

Eléphant du PHP | 451 Messages

10 déc. 2011, 17:45

Moi PDO me retourne un tableau comme ceci:
array(2) {
  [0]=>
  object(stdClass)#10 (34) {
   ["id"]=>
    string(1) "4"
    ["title"]=>
    string(4) "test"
    ["photos"]=>
    string(0) ""
    ["ingred"]=>
    string(14) "50g de beurre "
    ["etape"]=>
    string(4) "test"
    ["astuce"]=>
    string(0) ""
    ["vin"]=>
    string(0) ""
    ["online"]=>
    string(1) "1"
    ["slug"]=>
    NULL
    ["category_id"]=>
    string(1) "2"
    ["user_id"]=>
    string(1) "1"
    ["created_at"]=>
    string(19) "2011-12-05 22:37:55"
    ["modified_at"]=>
    NULL
    ["username"]=>
    string(5) "admin"
    ["password"]=>
    string(40) "d033e22ae348aeb5660fc2140aec35850c4da997"
    ["mail"]=>
    string(14) "[email protected]"
    ["firstname"]=>
    string(0) ""
    ["lastname"]=>
    string(0) ""
    ["born"]=>
    string(0) ""
    ["sexe"]=>
    string(0) ""
    ["website"]=>
    string(0) ""
    ["pays"]=>
    string(0) ""
    ["ville"]=>
    string(0) ""
    ["bio"]=>
    string(0) ""
    ["level"]=>
    string(1) "1"
    ["active"]=>
    string(1) "1"
    ["ban"]=>
    string(1) "0"
    ["delete"]=>
    string(1) "0"
    ["name"]=>
    string(14) "Administrateur"
    ["color"]=>
    string(6) "dc143c"
    ["order"]=>
    string(2) "10"
    ["recette_id"]=>
    string(1) "5"
    ["message"]=>
    string(4) "test"
    ["created"]=>
    string(19) "2011-12-10 10:26:53"
  }
  [1]=>
  object(stdClass)#11 (34) {
   ["id"]=>
    string(1) "5"
    ["title"]=>
    string(4) "test"
    ["photos"]=>
    string(0) ""
    ["ingred"]=>
    string(14) "50g de beurre "
    ["etape"]=>
    string(4) "test"
    ["astuce"]=>
    string(0) ""
    ["vin"]=>
    string(0) ""
    ["online"]=>
    string(1) "1"
    ["slug"]=>
    NULL
    ["category_id"]=>
    string(1) "2"
    ["user_id"]=>
    string(1) "1"
    ["created_at"]=>
    string(19) "2011-12-05 22:37:55"
    ["modified_at"]=>
    NULL
    ["username"]=>
    string(5) "admin"
    ["password"]=>
    string(40) "d033e22ae348aeb5660fc2140aec35850c4da997"
    ["mail"]=>
    string(14) "[email protected]"
    ["firstname"]=>
    string(0) ""
    ["lastname"]=>
    string(0) ""
    ["born"]=>
    string(0) ""
    ["sexe"]=>
    string(0) ""
    ["website"]=>
    string(0) ""
    ["pays"]=>
    string(0) ""
    ["ville"]=>
    string(0) ""
    ["bio"]=>
    string(0) ""
    ["level"]=>
    string(1) "1"
    ["active"]=>
    string(1) "1"
    ["ban"]=>
    string(1) "0"
    ["delete"]=>
    string(1) "0"
    ["name"]=>
    string(14) "Administrateur"
    ["color"]=>
    string(6) "dc143c"
    ["order"]=>
    string(2) "10"
    ["recette_id"]=>
    string(1) "5"
    ["message"]=>
    string(4) "test"
    ["created"]=>
    string(19) "2011-12-10 10:27:58"
  }
}
 
Et je voudrais le transformer comme ceci:
object(stdClass) {
     ["Recette"] => object(stdClass) {
          ["id"] => "4"
          ["img"] => "monimage.jpg"
          ["user_id"] => "1"
     }
     ["User"] => object(stdClass) {
          ["id"] => "1"
          ["level"] =>  "1"
     }
     ["Group"] => object(stdClass) {
          ["id"] => "1"
          ["rang"] => "admin"
          ["color"] => "ff0000"
     }
     ["Comment"] => object(stdClass) {
            [0] => object(stdClass) {
                  ["id"] => "1"
                  ["recette_id"] => "5"
                  ["user_id"] => "1"
                  ["message"] => "test"
                  ["created"] => "2011-12-10 10:26:53"
            }
            [1] => object(stdClass) {
                  ["id"] => "2"
                  ["recette_id"] => "5"
                  ["user_id"] => "1"
                  ["message"] => "test"
                  ["created"] => "2011-12-10 10:26:53"
            }
            [2] => object(stdClass) {
                  ["id"] => "3"
                  ["recette_id"] => "5"
                  ["user_id"] => "1"
                  ["message"] => "test"
                  ["created"] => "2011-12-10 10:26:53"
            }
     }
}
 
Mais il faut que ça le fasse suivant la requête que j'envoie à mon model.

Je cherche un peu à faire comme cakephp car lui il renvoie un tableau bien structurer comme je voudrais et il le fait à la volée.

J'ai regarder un peu leur code mais ils vont de classe en classe et j'ai pas réussi à trouver comment ils font.

ViPHP
xTG
ViPHP | 7331 Messages

10 déc. 2011, 20:17

Relis mon post et essayes de comprendre mon code.

Eléphant du PHP | 451 Messages

10 déc. 2011, 23:44

J'ai déjà les classes Recettes, Users, Groups, RecettesComments qui extends de model la ou est situé ma fonction find().

Dans mon controller je fais ceci:
class RecettesController extends Controller {
    
    function view($slug, $id) {
        $this->loadModel('Recettes');
        
        $r = $this->Recettes->find(array(
            'conditions' => array('Recettes.id' => $id),
            'joins' => array(
                array(
                    'table' => 'users',
                    'type' => 'left',
                    'conditions' => 'Recettes.user_id = Users.id'
                ),
                array(
                    'table' => 'groups',
                    'type' => 'left',
                    'conditions' => 'Users.level = Groups.id'
                ),
                array(
                    'table' => 'recettescomments',
                    'type' => 'left',
                    'conditions' => 'Recettes.id = Recettescomments.recette_id'
                )
             )
        ));
        
        if(empty($r)) {
            $this->e404();
        }

        $this->vars['post'] = $r;

        $this->render('view');
    }
}
Donc si je comprends bien je dois créer une fonction static dans mes models Recettes, Users, Groups, RecettesComments ou je peux la créer à la volée?

Et comment détecter quel champs va quel model car le champs id existe dans Recettes, Users, Groups, RecettesComments donc comment faire pour mettre les id correspondante dans le bon model associé?

Et après comment faire pour que le find() me retourne le tableau désiré?

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

11 déc. 2011, 02:28

salut,

en gros il va falloir que tu parse le retour. pour cela il faut que tu indique quelque part quel champs va avec quelle table.

donc la soit tu te étend la classe PDO pour ajouter cette fonctionnalités (tu passe un tableau en paramètre qui indique les champs des tables et tu parse le retour de PDO en fonction du tableau).

bon par contre ça va être anti performant ton affaire :)

tu peux tenter un truc comme ça
<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

class myPDO extends PDO {
    private $tables = array();
    private $typeTable = array();
    /*
     * table [nomtable] = unique /multiple
     */
    
    public function __construct($dsn, $username, $passwd, $options) {
        parent::__construct($dsn, $username, $passwd, $options);
    }
    
    public function setTable($tbl,$type){
        $this->table        = $tbl;
        $this->typeChamp    = $type;
    }
    public function myQuery($requete) {
        $result = $this->query($requete);
        $data = $result->fetchAll(PDO::FETCH_OBJ);
        $ret = new stdClass();
        foreach($data as $ligne){
            foreach ($ligne as $value){
                /*
                 * tables = array [
                 * nomtable array [
                 *              champ1
                 *              champ2
                 *          ]
                 *      ]
                 */
                foreach ($this->tables as $tbl => $listeChamp ){
                    $x = new stdClass();
                    foreach($listeChamp as $c){
                        $x->$c = $value->$c;
                    }
                    if ($this->typeTable[$tbl] == 'unique' && (isset($ret->$tbl) === false )){
                        $ret->$tbl = $$value;
                    }
                    else{
                        //cas multiple
                        $ret->$tbl[] = $value;
                    }
                }
            }
        }
        $data->close();
        return $ret;
    }
}
$tbl = array(
        'recette'=> array(
            'id','img','user_id'
        ),
        'User' => array(
            'id','level'
        )
    //etc
    );
// utilisation
$type = array('recette'=>'unique','User'=>'unique','Comment'=> 'multiple');//etc
$db = new myPDO($dsn, $username, $passwd, $options);
$db->setTable($tbl, $type);
$retour = $db->myQuery($requete);
?>
pas testé, si tu me file les tables et un jeux de données je pourrais p'tet tester demain.

le principe est la, tu indique comment sont faites les tables et tu boucle sur les résultats pour créer ton objet comme tu le souhaite.

@+
Il en faut peu pour être heureux ......

Eléphant du PHP | 451 Messages

11 déc. 2011, 11:48

J'ai essayé ton code Moogli mais ça me retourne un tableau bizarre le voici:
object(stdClass)#11 (4) {
  ["Recettes"]=>
  array(1) {
    [0]=>
    string(19) "2011-12-10 10:27:58"
  }
  ["User"]=>
  array(1) {
    [0]=>
    string(19) "2011-12-10 10:27:58"
  }
  ["Group"]=>
  array(1) {
    [0]=>
    string(19) "2011-12-10 10:27:58"
  }
  ["Comments"]=>
  array(1) {
    [0]=>
    string(19) "2011-12-10 10:27:58"
  }
}
Voici ma bdd:
--
-- Structure de la table `groups`
--

CREATE TABLE `groups` (
  `id` int(2) NOT NULL AUTO_INCREMENT,
  `name` varchar(40) NOT NULL,
  `color` varchar(6) NOT NULL,
  `order` int(2) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;

--
-- Contenu de la table `groups`
--

INSERT INTO `groups` VALUES(1, 'Administrateur', 'dc143c', 10);
INSERT INTO `groups` VALUES(2, 'Membre', '6495ed', 0);

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

--
-- Structure de la table `recettes`
--

CREATE TABLE `recettes` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) DEFAULT NULL,
  `photos` text NOT NULL,
  `ingred` text,
  `etape` text NOT NULL,
  `astuce` text NOT NULL,
  `vin` text NOT NULL,
  `online` enum('0','1','2','3','4') DEFAULT '0',
  `slug` varchar(255) DEFAULT NULL,
  `category_id` int(11) DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `modified_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_recettes_users` (`user_id`),
  KEY `fk_recettes_recettes_category1` (`category_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;

--
-- Contenu de la table `recettes`
--

INSERT INTO `recettes` VALUES(1, 'Souflé au fromage', '', '', 'Faites fondre le beurre, mélangez y la farine, et faites cuire 1 à 2 minutes.;||;Puis mouillez avec le lait, et remuez sur le feu jusqu''au premier bouillon. Laissez mijoter cette sauce qui doit être très épaisse. \r\nAssaisonnez de sel, poivre, noix de muscade râpée, puis retirez du feu. \r\n;||;Ajoutez les jaunes d''oeufs, et évitez la cuisson. \r\nMontez les blancs en neige ferme. Mélangez les dans la casserole avec les 100g de gruyère râpé. Mélangez vigoureusement et versez la préparation dans un plat à soufflé, beurré et poudré de fromage.;||;Faites cuire au four 20 minutes à feu modéré. Servez aussitôt sur un nid de salades composées. ', 'test astuce', 'voilà le vin', '1', NULL, 8, 1, '2011-09-25 17:10:08', NULL);
INSERT INTO `recettes` VALUES(2, 'Blinis', '', '', 'Dans un grand saladier, mélangez la farine de froment et la levure. Ajoutez 2 c. à soupe d''eau tiède. Travaillez bien pour obtenir une pâte lisse et homogène.\r\n\r\nCouvrez et laissez reposer 10 minutes.\r\n\r\nIncorporez a la pâte la farine de sarrasin, 1 oeuf, le sucre et le sel. Battez au fouet jusqu''à obtention d''un mélange onctueux. Couvrez et laissez reposer pendant 20 minutes.\r\n\r\nAjoutez à la pâte le lait chaud et la crème fraîche.\r\n\r\nPrenez les 2 oeufs restants ; séparez les blancs des jaunes. Mélangez les jaunes à la pâte.\r\n\r\nChauffez de l''huile dans une poêle. Jetez-y des doses successives de pâte que vous faites cuire sur feu moyen. Égouttez les blinis sur du papier absorbant et réservez-les au chaud.\r\n\r\nGarnissez-les de tarama, d''oeufs de saumon, de tranches d''esturgeon et/ou de caviar.', 'Ces blinis gagneront en légèreté si vous remplacez la crème fraîche par du yaourt au lait entier. La confection des blinis est simplifiée par le recours aux petites poêles, de la taille d''une crêpe, et donc réservées à cette préparation culinaire.', '', '1', NULL, 5, 1, '2011-09-25 23:41:14', NULL);
INSERT INTO `recettes` VALUES(3, 'Verrines de saumon fumé', '', '6 tranches de saumon fumé;||;1 c. à café d’huile d’olive;||;3 c. à soupe de ricotta;||;3 c. à soupe de crème épaisse légère;||;¼ de botte d’aneth;||;¼ de botte de ciboulette;||;sel, poivre;||;', 'Hachez l’aneth, ciselez la ciboulette.\r\n\r\nHachez le saumon fumé et mélangez avec l’aneth hachée, l’huile d’olive et le poivre.\r\n\r\nMélangez la ricotta, la crème, la ciboulette et le sel.\r\n\r\nRemplissez les verrines, en commençant par la crème, puis finissez par le saumon haché.', '', '', '1', NULL, 2, 2, '2011-09-25 23:37:20', NULL);
INSERT INTO `recettes` VALUES(4, 'test', '', 'test', '', '', '', '1', NULL, 0, 1, '2011-12-04 20:32:20', NULL);
INSERT INTO `recettes` VALUES(5, 'test', '', '50g de beurre ', 'test', '', '', '1', NULL, 2, 1, '2011-12-05 22:37:55', NULL);

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

--
-- Structure de la table `recettescomments`
--

CREATE TABLE `recettescomments` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `recette_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `message` text NOT NULL,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;

--
-- Contenu de la table `recettescomments`
--

INSERT INTO `recettescomments` VALUES(1, 1, 2, 'Un commentaire de test', '2011-12-09 22:03:22');
INSERT INTO `recettescomments` VALUES(2, 1, 2, 'un autre', '2011-12-09 22:27:11');
INSERT INTO `recettescomments` VALUES(3, 1, 1, 'je test', '2011-12-10 10:21:06');
INSERT INTO `recettescomments` VALUES(4, 5, 1, 'test', '2011-12-10 10:26:53');
INSERT INTO `recettescomments` VALUES(5, 5, 1, 'test', '2011-12-10 10:27:58');

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

--
-- Structure de la table `users`
--

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(45) DEFAULT NULL,
  `password` varchar(45) DEFAULT NULL,
  `mail` varchar(45) DEFAULT NULL,
  `firstname` varchar(255) NOT NULL,
  `lastname` varchar(255) NOT NULL,
  `born` varchar(20) NOT NULL,
  `sexe` varchar(20) NOT NULL,
  `website` varchar(255) NOT NULL,
  `pays` varchar(40) NOT NULL,
  `ville` varchar(255) NOT NULL,
  `bio` text NOT NULL,
  `level` int(3) NOT NULL,
  `active` enum('0','1','2') DEFAULT NULL,
  `ban` enum('0','1') NOT NULL DEFAULT '0',
  `delete` enum('0','1') NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

--
-- Contenu de la table `users`
--

INSERT INTO `users` VALUES(1, 'admin', 'd033e22ae348aeb5660fc2140aec35850c4da997', '[email protected]', '', '', '', '', '', '', '', '', 1, '1', '0', '0');
INSERT INTO `users` VALUES(2, 'test', 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3', '[email protected]', '', '', '', '', '', '', '', '', 2, '1', '0', '0');
Voici toute ma page model:
class Model {
    
    static $sql_debug = array();

    static $connect = array();
    
    public $db;
    public $database = 'default';
    
    public $table = false;
    public $primaryKey = 'id';
    public $id;
    
    public $data = null;
    
    public $validate = false;

    public $currentModel = false;
    public $validateError = false;

    private $tables = array();
    private $typeTable = array();



    public function __construct($request = null) {
        $this->request = $request;

        $conf = Configure::read('Model.database');
        $conf = $conf[$this->database];

        $this->currentModel = get_class($this);

        if($this->table === false) {
            $this->table = strtolower(get_class($this));
        }

        if(isset(Model::$connect[$this->database])) {
            $this->db = Model::$connect[$this->database];
            
            return true;
        }
        
        try {
            $dbh = new PDO('mysql:host='.$conf['hostname'].';dbname='.$conf['database'].';', 
                    $conf['username'], 
                    $conf['password'],
                    array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES '.$conf['charset'])
            );
            
            $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
            
            Model::$connect[$this->database] = $dbh;
            
            $this->db = $dbh;
        } catch (PDOException $e) {
            if(Connfig::$debug == 1) {
                die("Erreur SQL: " . $e->getMessage());
            } else {
                die('Impossible de ce connecter à la base de donnée.');
            }
        }
    }


    public function _validates($action, $tab) {
        $valid = array();
        
        foreach($tab as $field => $rules) {
            if(isset($rules['message'])) {
                $message = $rules['message'];
                unset($rules['message']);
            } else {
                $message = null;
            }

            if(!is_array($rules) || (is_array($rules) && isset($rules['rule']))) {
                $rules = array('rule' => $rules);
            }

            $rules = current($rules);

            foreach($rules['rule'] as $k => $v) {
                $d = get_object_vars($this->data);

                $valid[] = call_user_func_array(array('Validate', $k), array($field, $v, $d[$field], (isset($message[$k])) ? $message[$k] : null));
            }
        }
        
        if(in_array(false, $valid, true)) {
            $this->validateError[] = call_user_func(array('Validate', '_return'));

            return false;
        } else {
            return true;
        }
    }


    public function validations($data) {
        $valid = true;

        if($this->validate) {
            $this->data = $data;
            $d = get_object_vars($this->data);
            
            if($this->request->prefix) {
                if($this->request->action != 'edit') {
                    $a = $this->request->action;
                } elseif($this->request->action == 'edit' && $d[$this->primaryKey] != null) {
                    $a = $this->request->prefix.'_edit';
                } else {
                    $a = $this->request->prefix.'_add';
                }
            } else {
                if($this->request->action != 'edit') {
                    $a = $this->request->action;
                } elseif($this->request->action == 'edit' && $d[$this->primaryKey] != null) {
                    $a = 'edit';
                } else {
                    $a = 'add';
                }
            }

            if(isset($this->validate[$a])) {
                $valid = $this->_validates($a, $this->validate[$a]);
            }
        }

        return $valid;
    }


    public function setTable($tbl, $type) {
        $this->tables = $tbl;
        $this->typeChamp = $type;
    }

    
    public function find($d = null) {
        $valid = true;

        if($this->request->data) {
            $valid = $this->validations($this->request->data);
        }

        if($valid) {
            $sql = 'SELECT ';
            
            if(isset($d['fields'])) {
                if(isset($d['join'])) {
                    
                }
                
                if(!is_array($d['fields'])) {
                    $sql .= $d['fields'];
                } else {
                    $sql .= implode(', ', $d['fields']);
                }
            } else {
                $sql .= '*';
            }
            
            $sql .= ' FROM '.$this->table.' as '.get_class($this);
            
            if(isset($d['joins'])) {
                for($i = 0; $i < count($d['joins']); $i++) {
                    $sql .= ' '.strtoupper($d['joins'][$i]['type']).' JOIN '.strtolower($d['joins'][$i]['table']).' '.ucfirst($d['joins'][$i]['table']);
                    $sql .= ' ON '.$d['joins'][$i]['conditions'];
                }
            }
            
            if(isset($d['conditions'])) {
                $sql .= ' WHERE ';
                
                if(!is_array($d['conditions'])) {
                    $sql .= $d['conditions'];
                } else {
                    $cond = array();
                    
                    foreach ($d['conditions'] as $k => $v) {
                        $v = $this->db->quote($v);
                        
                        $cond[] = "$k = $v";
                    }
                    
                    $sql .= implode(' AND ', $cond);
                }
            }
            
            if(isset($d['limit'])) {
                $sql .= ' LIMIT '.$d['limit'];
            }
            
            if(isset($d['order_by'])) {
                $sql .= ' ORDER BY '.$d['order_by'];
            }

            //$this->sql_debug[] = $sql;
            //debug($sql);
            //die();
            $pre = $this->db->query($sql);
            $pre->execute();

            $data = $pre->fetchAll(PDO::FETCH_OBJ);

            $ret = new stdClass();

            foreach($data as $ligne){
                foreach ($ligne as $value){
                    foreach ($this->tables as $tbl => $listeChamp ){
                        $x = new stdClass();

                        foreach($listeChamp as $c){
                            $x->$c = $value->$c;
                        }

                        if ($this->typeTable[$tbl] == 'unique' && (isset($ret->$tbl) === false )){
                            $ret->$tbl = $$value;
                        } else {
                            $ret->$tbl = array($value);
                        }
                    }
                }
            }

            return $ret;
        } else {
            return false;
        }
    }

    
    public function findFirst($d) {
        $valid = true;

        if($this->request->data) {
            $valid = $this->validations($this->request->data);
        }
        
        if($valid) {
            return current($this->find($d));
        } else {
            return false;
        }
    }

    
    public function findCount($d = null) {
        $res = $this->findFirst(array(
            'conditions' => $d['conditions'],
            'fields' => 'COUNT('.$this->primaryKey.') as count'
        ));
        
        return $res->count;
    }

    
    public function save($data) {
        $valid = $this->validations($data);

        if($valid) {
            $key = $this->primaryKey;
            
            $fields = array();
            
            foreach($data as $k => $v) {
                if($k != $key) {
                    $fields[] = "`$k` = '$v'";
                }
            }
                
            if(isset($data->$key) && !empty($data->$key)) {
                $act = 'update';
                
                $sql = 'UPDATE '.$this->table.' SET '.implode(', ', $fields).' WHERE `'.$key.'` = '.$data->$key;
                
                $this->id = $data->$key;
            } else {
                $act = 'insert';
                
                if(isset($data->$key)) {
                    unset($data->$key);
                }
                
                $sql = 'INSERT INTO '.$this->table.' SET '.implode(', ', $fields);
            }
            //debug($sql);die();
            $pre = $this->db->prepare($sql);
            $pre->execute();
                   
            if($act == 'insert') {
                $this->id = $this->db->lastInsertId();
            }

            return true;
        } else {
            return false;
        }
    }

    
    public function delete($id) {
        $sql = "DELETE FROM {$this->table} WHERE {$this->primaryKey} = {$id}";
        $this->db->query($sql);
    }

}
Et voici une partie de mon controller:
    function view($slug, $id) {
        $this->loadModel('Recettes');
        
        $tbl = array(
            'Recettes' => array(
                'id','img','user_id'
            ),
            'User' => array(
                'id','level'
            ),
            'Group' => array(
                'id','color'
            ),
            'Comments' => array(
                'id','message', 'date'
            )
        );

        $type = array('Recette' => 'unique', 'User' => 'unique', 'Group' => 'unique', 'Comment' => 'multiple');

        $this->Recettes->setTable($tbl, $type);
        
        $r = $this->Recettes->find(array(
            'conditions' => array('Recettes.id' => $id),
            'joins' => array(
                array(
                    'table' => 'users',
                    'type' => 'left',
                    'conditions' => 'Recettes.user_id = Users.id'
                ),
                array(
                    'table' => 'groups',
                    'type' => 'left',
                    'conditions' => 'Users.level = Groups.id'
                ),
                array(
                    'table' => 'recettescomments',
                    'type' => 'left',
                    'conditions' => 'Recettes.id = Recettescomments.recette_id'
                )
             )
        ));
        
        if(empty ($r)) {
            $this->e404();
        }

        $this->vars['post'] = $r;

        $this->render('view');
    }

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

11 déc. 2011, 15:16

fiou s'pas simple, deux heures pour modifier et ajouter les classes manquantes et encore j'ai pas mal commenté le code pour pas me prendre la tête ^^

manque la table Model
ta requete généré
SELECT * FROM model as Model LEFT JOIN users Users ON Recettes.user_id = Users.id LEFT JOIN groups Groups ON Users.level = Groups.id LEFT JOIN recettescomments Recettescomments ON Recettes.id = Recettescomments.recette_id WHERE Recettes.id = '1'
n'est pas bonne il ne devrait pas y avoir de ' autour de l'id, c'est un chiffre (un entier même) et non une chaine de caractère :)

Dans la classe modèle pourquoi utilise à la fois $db et $connect ? au final c'est la même chose :)

Il y avait pas mal de bug dans ma classe, vu que c'était du pas testé ^^

Le problème étant de filer un objet pour les cas "multiple".
j'ai donc créer un objet pour cela qui implémente l'interface iterator.
<?php
class multiStatement implements Iterator{
    /**
     *collection d'objet
     * @var array 
     */
   private $data = array();
   /**
    * position que l'on doit afficher lors du parcourt avec foreach
    * @var int
    */
   private $position;
   
   public function __construct() {
        $this->position = 0;
    }
    /**
     *Ajout d'un item dans la collection
     * @param stdclass $o 
     */
   public function put(stdClass $o) {
       $this->data[] = $o;
       return true;
   }
   /* 
    * Les méthodes utilisable avec foreach, implementation de l'interface iterator
    */
    function rewind() {
        $this->position = 0;
    }

    function current() {
        return $this->data[$this->position];
    }

    function key() {
        return $this->position;
    }

    function next() {
        ++$this->position;
    }

    function valid() {
        return isset($this->data[$this->position]);
    }
}
?>
cette classe est assez basique :
- put permet d'insérer des objets (à la limite ce que tu veux mais faut virer le typage dans la méthode put ;)).
- pour récupérer les infos un foreach sur l'objet et ça roule :) pour plus d'info l'interface iterator

exemple de test :
class myPDO extends PDO {
    private $tables = array();
    private $typeTable = array();

    public function __construct($dsn, $username, $passwd, $options) {
        parent::__construct($dsn, $username, $passwd, $options);
    }
    
    public function setTable($tbl,$type){
        $this->tables        = $tbl;
        $this->typeTable    = $type;
    }
    public function myQuery($requete) {
        $result = $this->query($requete);
        $data = $result->fetchAll(PDO::FETCH_OBJ);
        $result->closeCursor();
        $ret = new stdClass();
        $i = 0 ;
        foreach($data as $ligne){
            foreach ($this->tables as $tbl => $listeChamp ){
                $x = new stdClass();
                foreach ($listeChamp as $c){
                    $x->$c = $ligne->$c;
                }
                if ($this->typeTable[$tbl] == 'unique' ){ 
                    if (isset($ret->$tbl) === false)
                        $ret->{$tbl} = $x;
                }
                else{
                    //cas multiple
                    if (!isset($ret->$tbl)){   
                        $ret->$tbl = new multiStatement();
                    }
                    $ret->$tbl->put($x);
                }
            }
            $i++;
        }
        return $ret;
    }
}
$tbl = array(
        'news'=> array(
            'idnews', 'idNewser','titre','newsdate'
        ),
        'newscomments' => array(
            'pseudo', 'mail', 'comment', 'dc'
        )
    //etc
    );
$dsn = 'mysql:host=localhost;dbname=phpjungle';
$username = 'root';
$passwd = 'yyRu2TKEvyYpzFLK';
$options=null;
$type = array('news'=>'unique','newscomments'=> 'multiple');//etc
$db = new myPDO($dsn, $username, $passwd, $options);
$db->setTable($tbl, $type);
$requete = 'SELECT news.id as idnews, idNewser,titre, 
    date_format(dateNews,\'%d-%m-%Y\') as newsdate, pseudo, mail, comment, 
date_format(datecomment,\'%d-%m-%Y\') as dc
 FROM news
join newscomments on news.id = newscomments.idnews
where news.id = 4';
$retour = $db->myQuery($requete);
var_dump($retour);
?>
Ce qui donne
object(stdClass)#5 (2) {
["news"]=>
object(stdClass)#6 (4) {
["idnews"]=>
string(1) "4"
["idNewser"]=>
string(1) "1"
["titre"]=>
string(28) "Derni&egrave;re ligne droite"
["newsdate"]=>
string(10) "25-07-2007"
}
["newscomments"]=>
object(multiStatement)#8 (2) {
["data":"multiStatement":private]=>
array(2) {
[0]=>
object(stdClass)#7 (4) {
["pseudo"]=>
string(11) ""
["mail"]=>
string(0) ""
["comment"]=>
string(99) ""
["dc"]=>
string(10) "04-04-2008"
}
[1]=>
object(stdClass)#10 (4) {
["pseudo"]=>
string(11) ""
["mail"]=>
string(0) ""
["comment"]=>
string(415) ""
["dc"]=>
string(10) "21-01-2010"
}
}
["position":"multiStatement":private]=>
int(0)
}
}
Qui a priori ressemble a ce que tu souhaite ?

le truc c'est de bien définir les deux tableaux au départ sinon la merde :)

@+
Il en faut peu pour être heureux ......

Eléphant du PHP | 451 Messages

11 déc. 2011, 16:08

Pour le $connect et $db pour vérifier si une connection existe déjà pour la réutiliser pour éviter de relancer une connection à chaque chargement de model car si je fais ceci:
$this->loadModel('Recettes');
$this->loadModel('Users');
$this->loadModel('RecettesComments');
Et bien les 3 models utiliseront la même connection au lieu d'en refaire une à chaque fois que je fais un loadModel.

Voici le retour avec ta dernière classe:
object(stdClass)#11 (4) {
  ["Recettes"]=>
  object(multiStatement)#13 (2) {
    ["data":"multiStatement":private]=>
    array(2) {
      [0]=>
      object(stdClass)#12 (3) {
        ["id"]=>
        string(1) "4"
        ["ingred"]=>
        string(14) "50g de beurre "
        ["user_id"]=>
        string(1) "1"
      }
      [1]=>
      object(stdClass)#20 (3) {
        ["id"]=>
        string(1) "5"
        ["ingred"]=>
        string(14) "50g de beurre "
        ["user_id"]=>
        string(1) "1"
      }
    }
    ["position":"multiStatement":private]=>
    int(0)
  }
  ["User"]=>
  object(multiStatement)#15 (2) {
    ["data":"multiStatement":private]=>
    array(2) {
      [0]=>
      object(stdClass)#14 (2) {
        ["id"]=>
        string(1) "4"
        ["level"]=>
        string(1) "1"
      }
      [1]=>
      object(stdClass)#21 (2) {
        ["id"]=>
        string(1) "5"
        ["level"]=>
        string(1) "1"
      }
    }
    ["position":"multiStatement":private]=>
    int(0)
  }
  ["Group"]=>
  object(multiStatement)#17 (2) {
    ["data":"multiStatement":private]=>
    array(2) {
      [0]=>
      object(stdClass)#16 (2) {
        ["id"]=>
        string(1) "4"
        ["color"]=>
        string(6) "dc143c"
      }
      [1]=>
      object(stdClass)#22 (2) {
        ["id"]=>
        string(1) "5"
        ["color"]=>
        string(6) "dc143c"
      }
    }
    ["position":"multiStatement":private]=>
    int(0)
  }
  ["Comments"]=>
  object(multiStatement)#19 (2) {
    ["data":"multiStatement":private]=>
    array(2) {
      [0]=>
      object(stdClass)#18 (3) {
        ["id"]=>
        string(1) "4"
        ["message"]=>
        string(4) "test"
        ["date"]=>
        NULL
      }
      [1]=>
      object(stdClass)#23 (3) {
        ["id"]=>
        string(1) "5"
        ["message"]=>
        string(4) "test"
        ["date"]=>
        NULL
      }
    }
    ["position":"multiStatement":private]=>
    int(0)
  }
}
Déjà les id ne sont pas bien récupéré car c'est toujours 4 et 5 hors que dans le group et user c'est 1.

Donc la je pense que je dois modifier mes champs exemple:
id de Recettes devient recette_id
id de Users devient user_id
id de Groups devient group_id

Et quand il y a une relation comme dans RecettesComments
au lieu de recette_id je devrais mettre un truc pour savoir que ce champs est lié avec avec le champ recette_id de la table Recettes mais je sais pas quoi mettre pour préciser qu'il y a une relation.



Et dans ton code je sais que je devrais mettre un current() quelque part pour que les tableau qui sont en inique soit pas répété mais je sais pas ou le mettre

Eléphant du PHP | 451 Messages

11 déc. 2011, 16:59

En faites c'est moi qui avait fait une erreur c'est pour ça que tout était en double dans la fonction setTable j'avais mit typeChamp au lieu de typeTable.

Donc tout est rentré dans l'ordre merci xTG et Moogli pour votre aide.

Là je suis en train de modifier un peu pour que ça respecte plus mon format.

Et je vais voir après pour essayer que ça se fasse tout seul sans que je precise un type et les champs.

Eléphant du PHP | 451 Messages

11 déc. 2011, 17:40

Par contre j'ai une petite question.

Maintenant pour afficher mes commentaires je dois faire:
$post->UsersComments->data[$i]->username;
Et je voudrais savoir si y a pas un moyen de faire:
$post->UsersComments->$i->username;
J'ai essayé ceci:
            foreach($data as $ligne){
                foreach($this->tables as $tbl => $listeChamp) {
                    $x = new stdClass();

                    foreach($listeChamp as $c){
                        $x->$c = $ligne->$c;
                    }

                    if($this->typeTable[$tbl] == 'unique') { 
                        if(empty($ret->$tbl)) {
                            $ret->{$tbl} = $x;
                        } else {
                            continue;
                        }
                    } else {
                        if(!isset($ret->$tbl)){   
                            $ret->$tbl = new multiStatement();
                        }

                        $ret->$tbl->put($x);
                    }

                    if($ret->$tbl->data) {
                        $ret->$tbl = $ret->$tbl->data;
                    }
                }

                $i++;
            }
Mais le $ret->$tbl = $ret->$tbl->data; me met une erreur: Fatal error: Call to a member function put() on a non-object

Vous y auriez une solution ou je suis obligé de laisser telle quelle?

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

11 déc. 2011, 19:48

avec un foreach sur les "multiStatement" tu n'aura pas de soucis, c'est a cela que sert l'interface iterator :)

sinon un stdClass mais faut que tu gère l’incrément des infos


@+
Il en faut peu pour être heureux ......

Eléphant du PHP | 451 Messages

11 déc. 2011, 20:21

J'ai réussi comme ceci:
foreach($ret as $k => $v) {
                if($v->data) {
                    $ret->$k = $this->array2object($v->data);
                }
            }
Et ma fonction array2object:
    public function array2object(array $array) {
        $object = new stdClass();

        foreach($array as $key => $value) {
            if(is_array($value)) {
                $object->$key = array2object($value);
            } else {
                $object->$key = $value;
            }
        }

        return $object;
    }
Et j'ai des tableaux comme ceci:
 ["Groups"]=>
  object(stdClass)#13 (2) {
    ["groups_id"]=>
    string(1) "2"
    ["color"]=>
    string(6) "6495ed"
  }
  ["RecettesComments"]=>
  object(stdClass)#18 (1) {
    ["0"]=>
    object(stdClass)#14 (3) {
      ["recettescomments_id"]=>
      NULL
      ["message"]=>
      NULL
      ["created"]=>
      NULL
    }
  }
  ["UsersComments"]=>
  object(stdClass)#15 (1) {
    ["0"]=>
    object(stdClass)#16 (3) {
      ["user_id"]=>
      NULL
      ["username"]=>
      NULL
      ["group_id"]=>
      NULL
    }
  }
Et ben comment faire pour que si le tableau ne contient que des null alors on le modifie pour qu'il ressemble à ceci:
 ["Groups"]=>
  object(stdClass)#13 (2) {
    ["groups_id"]=>
    string(1) "2"
    ["color"]=>
    string(6) "6495ed"
  }
  ["RecettesComments"]=>
  object(stdClass)#18 (1) {
    NULL
  }
  ["UsersComments"]=>
  object(stdClass)#15 (1) {
    NULL
  }
Cela me servira à faire:
if($post->RecettesComments != null) {} 
Au lieu de:
if($post->RecettesComments->data[0]->message != null) {}