IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

PHP & Base de données Discussion :

Convertir en PDO [PDO]


Sujet :

PHP & Base de données

  1. #1
    Membre actif Avatar de dancom5
    Homme Profil pro
    Inscrit en
    Janvier 2010
    Messages
    808
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55

    Informations forums :
    Inscription : Janvier 2010
    Messages : 808
    Points : 241
    Points
    241
    Par défaut Convertir en PDO
    Bonjour.

    J'ai besoin d'aide pour convertir la parti mysql en version PDO de ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    <?php
    if($_POST['page'])
    {
    $page = $_POST['page'];
    $cur_page = $page;
    $page -= 1;
    $per_page = 1;
    $previous_btn = true;
    $next_btn = true;
    $first_btn = true;
    $last_btn = true;
    $start = $page * $per_page;
    include"db.php";
     
    $query_pag_data = "SELECT * from crud_categorie LIMIT $start, $per_page";
    $result_pag_data = mysql_query($query_pag_data) or die('MySql Error' . mysql_error());
    $msg = "";
    while ($row = mysql_fetch_array($result_pag_data)) {
    $htmlmsg=htmlentities($row['categorie']);
        $msg .= "<li><b>" . $row['id'] . "</b> " . $htmlmsg . "</li>";
    }
    $msg = "<div class='data'><ul>" . $msg . "</ul></div>"; // Content for Data
     
     
    /* --------------------------------------------- */
    $query_pag_num = "SELECT COUNT(*) AS count FROM crud_categorie";
    $result_pag_num = mysql_query($query_pag_num);
    $row = mysql_fetch_array($result_pag_num);
    $count = $row['count'];
    $no_of_paginations = ceil($count / $per_page);
     
    /* ---------------Calculating the starting and endign values for the loop----------------------------------- */
    if ($cur_page >= 7) {
        $start_loop = $cur_page - 3;
        if ($no_of_paginations > $cur_page + 3)
            $end_loop = $cur_page + 3;
        else if ($cur_page <= $no_of_paginations && $cur_page > $no_of_paginations - 6) {
            $start_loop = $no_of_paginations - 6;
            $end_loop = $no_of_paginations;
        } else {
            $end_loop = $no_of_paginations;
        }
    } else {
        $start_loop = 1;
        if ($no_of_paginations > 7)
            $end_loop = 7;
        else
            $end_loop = $no_of_paginations;
    }
    /* ----------------------------------------------------------------------------------------------------------- */
    $msg .= "<div class='pagination'><ul>";
     
    // FOR ENABLING THE FIRST BUTTON
    if ($first_btn && $cur_page > 1) {
        $msg .= "<li p='1' class='active'> << </li>";
    } else if ($first_btn) {
        $msg .= "<li p='1' class='inactive'> << </li>";
    }
     
    // FOR ENABLING THE PREVIOUS BUTTON
    if ($previous_btn && $cur_page > 1) {
        $pre = $cur_page - 1;
        $msg .= "<li p='$pre' class='active'> < </li>";
    } else if ($previous_btn) {
        $msg .= "<li class='inactive'> < </li>";
    }
    for ($i = $start_loop; $i <= $end_loop; $i++) {
     
        if ($cur_page == $i)
            $msg .= "<li p='$i' style='color:#fff;background-color:#000;' class='active'>{$i}</li>";
        else
            $msg .= "<li p='$i' class='active'>{$i}</li>";
    }
     
    // TO ENABLE THE NEXT BUTTON
    if ($next_btn && $cur_page < $no_of_paginations) {
        $nex = $cur_page + 1;
        $msg .= "<li p='$nex' class='active'> > </li>";
    } else if ($next_btn) {
        $msg .= "<li class='inactive'> > </li>";
    }
     
    // TO ENABLE THE END BUTTON
    if ($last_btn && $cur_page < $no_of_paginations) {
        $msg .= "<li p='$no_of_paginations' class='active'> >> </li>";
    } else if ($last_btn) {
        $msg .= "<li p='$no_of_paginations' class='inactive'> > </li>";
    }
    $goto = "<input type='text' class='goto' size='1' style='margin-top:-1px;margin-left:60px;'/><input type='button' id='go_btn' class='go_button' value='Go'/>";
    $total_string = "<span class='total' a='$no_of_paginations'>Page <b>" . $cur_page . "</b> / <b>$no_of_paginations</b></span>";
    $msg = $msg . "</ul>" . $goto . $total_string . "</div>";  // Content for pagination
    echo $msg;
    }
    Ce qui m'intéresse particulièrement, c'est la partie MYSQL pour avoir en PDO. J'apprécie.
    On oublie souvent la simplicité ou la base dans la vie: Maslow

  2. #2
    Modérateur
    Avatar de sabotage
    Homme Profil pro
    Inscrit en
    Juillet 2005
    Messages
    29 208
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Juillet 2005
    Messages : 29 208
    Points : 44 155
    Points
    44 155
    Par défaut
    Montre nous ce que tu as fait et on pourra t'aider a le corriger.
    N'oubliez pas de consulter les FAQ PHP et les cours et tutoriels PHP

  3. #3
    Membre actif Avatar de dancom5
    Homme Profil pro
    Inscrit en
    Janvier 2010
    Messages
    808
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55

    Informations forums :
    Inscription : Janvier 2010
    Messages : 808
    Points : 241
    Points
    241
    Par défaut
    ok. Tien, j'ai converti en PDO mais je ne suis pas sur que
    c'est tout correcte:

    Y'a peut-être place à amélioration au niveau PDO.

    Le résultat est la et fonctionne.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    <?php
    if($_POST['page'])
    {
    $page = $_POST['page'];
    $cur_page = $page;
    $page -= 1;
    $per_page = 1;
    $previous_btn = true;
    $next_btn = true;
    $first_btn = true;
    $last_btn = true;
    $start = $page * $per_page;
    include"db.php";
     
     
    // TUTO suivi sur : query http://www.phpeveryday.com/articles/PDO-Prepared-Statement-and-Bound-Values-P553.html
     
    $query_pag_data = "SELECT * from crud_categorie LIMIT $start, $per_page";
    $result_pag_data = $con->prepare($query_pag_data );
    $result_pag_data->execute();
    $result_pag_data->bindColumn(1, $id);
    $result_pag_data->bindColumn(2, $categorie);
    $result_pag_data->bindColumn(3, $description);
     
    $msg = "";
     
    while($result_pag_data->fetch()) {
     
        $msg .= "<li><b>" . $id . "</b> " . $categorie . "</li>";
     
    }
    $msg = "<div class='data'><ul>" . $msg . "</ul></div>"; // Content for Data
     
    /* --------------------------------------------- */
     
    $query_pag_num = "SELECT * FROM crud_categorie";
    $result_pag_num = $con->prepare( $query_pag_num );
    $result_pag_num->execute();
    $count = $result_pag_num->rowCount();
    echo $count;
     
    $no_of_paginations = ceil($count / $per_page);
     
    /* ---------------Calculating the starting and endign values for the loop----------------------------------- */
    if ($cur_page >= 7) {
        $start_loop = $cur_page - 3;
        if ($no_of_paginations > $cur_page + 3)
            $end_loop = $cur_page + 3;
        else if ($cur_page <= $no_of_paginations && $cur_page > $no_of_paginations - 6) {
            $start_loop = $no_of_paginations - 6;
            $end_loop = $no_of_paginations;
        } else {
            $end_loop = $no_of_paginations;
        }
    } else {
        $start_loop = 1;
        if ($no_of_paginations > 7)
            $end_loop = 7;
        else
            $end_loop = $no_of_paginations;
    }
    /* ----------------------------------------------------------------------------------------------------------- */
    $msg .= "<div class='pagination'><ul>";
     
    // FOR ENABLING THE FIRST BUTTON
    if ($first_btn && $cur_page > 1) {
        $msg .= "<li p='1' class='active'> << </li>";
    } else if ($first_btn) {
        $msg .= "<li p='1' class='inactive'> << </li>";
    }
     
    // FOR ENABLING THE PREVIOUS BUTTON
    if ($previous_btn && $cur_page > 1) {
        $pre = $cur_page - 1;
        $msg .= "<li p='$pre' class='active'> < </li>";
    } else if ($previous_btn) {
        $msg .= "<li class='inactive'> < </li>";
    }
    for ($i = $start_loop; $i <= $end_loop; $i++) {
     
        if ($cur_page == $i)
            $msg .= "<li p='$i' style='color:#fff;background-color:#000;' class='active'>{$i}</li>";
        else
            $msg .= "<li p='$i' class='active'>{$i}</li>";
    }
     
    // TO ENABLE THE NEXT BUTTON
    if ($next_btn && $cur_page < $no_of_paginations) {
        $nex = $cur_page + 1;
        $msg .= "<li p='$nex' class='active'> > </li>";
    } else if ($next_btn) {
        $msg .= "<li class='inactive'> > </li>";
    }
     
    // TO ENABLE THE END BUTTON
    if ($last_btn && $cur_page < $no_of_paginations) {
        $msg .= "<li p='$no_of_paginations' class='active'> >> </li>";
    } else if ($last_btn) {
        $msg .= "<li p='$no_of_paginations' class='inactive'> > </li>";
    }
    $goto = "<input type='text' class='goto' size='1' style='margin-top:-1px;margin-left:60px;'/><input type='button' id='go_btn' class='go_button' value='Go'/>";
    $total_string = "<span class='total' a='$no_of_paginations'>Page <b>" . $cur_page . "</b> / <b>$no_of_paginations</b></span>";
    $msg = $msg . "</ul>" . $goto . $total_string . "</div>";  // Content for pagination
    echo $msg;
    }
    On oublie souvent la simplicité ou la base dans la vie: Maslow

  4. #4
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Salut,

    As-tu un peu potassé la doc PDO parce que vu le code que tu montres, je ne le crois pas.

    Dans ce bout de code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    $query_pag_data = "SELECT * from crud_categorie LIMIT $start, $per_page";
    $result_pag_data = $con->prepare($query_pag_data );
    $result_pag_data->execute();
    $result_pag_data->bindColumn(1, $id);
    $result_pag_data->bindColumn(2, $categorie);
    $result_pag_data->bindColumn(3, $description);
    Je voudrais savoir où sont présentes dans le SQL les colonnes auxquelles tu rattaches des valeurs ?

  5. #5
    Modérateur
    Avatar de sabotage
    Homme Profil pro
    Inscrit en
    Juillet 2005
    Messages
    29 208
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Juillet 2005
    Messages : 29 208
    Points : 44 155
    Points
    44 155
    Par défaut
    Ca ne sert à rien d'utiliser la préparation des requêtes quand elles n'ont pas de paramètres

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    mysql_query($query_pag_data)
    =>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $con->query($query_pag_data)
    N'oubliez pas de consulter les FAQ PHP et les cours et tutoriels PHP

  6. #6
    Expert éminent sénior

    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2010
    Messages
    5 380
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2010
    Messages : 5 380
    Points : 10 410
    Points
    10 410
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $query_pag_num = "SELECT * FROM crud_categorie";
    $result_pag_num = $con->prepare( $query_pag_num );
    $result_pag_num->execute();
    $count = $result_pag_num->rowCount();
    echo $count;
    c'est pas optimisé, pour faire un simple comptage

    =>

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    $query_pag_num = "SELECT count(*) FROM crud_categorie";
    $result_pag_num = $con->query($query_pag_num);
    $count = $result_pag_num->fetchColumn();
    echo $count;

  7. #7
    Membre actif Avatar de dancom5
    Homme Profil pro
    Inscrit en
    Janvier 2010
    Messages
    808
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55

    Informations forums :
    Inscription : Janvier 2010
    Messages : 808
    Points : 241
    Points
    241
    Par défaut
    Merci pour vos réponses.

    rawsrc, je me base toujours en premier sur php.net pour tout ce qui
    touche PHP et MYSQL. Mais, en essayant de convertir, j'ai fais des
    erreurs et j'ai pas tout vu non plus. Je suis débutant en PDO.

    rawsrc, j'ai mal complété:

    sabotage, quand on parle de paramètres, c'est ceci?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ici->$result_pag_data->bindColumn(1, $id);
    là où j'ai mis ici?

    Les données sortait quand même. Mais, je veux faire les choses correcte.

    Le code faisant object de ce topic c'est un exemple de pagination que
    je tentais de comprendre pour convertir en PDO. C'est ajax, jquery.

    Mon code à moi, c'est celui la que je vais adapter pour ajouter
    la pagination.

    Je vois que ma page de pagination est directement sur la page.
    Alors qu'il faut que je fasse une page: load_data.php.

    Je vais avoir fort à faire.
    On oublie souvent la simplicité ou la base dans la vie: Maslow

  8. #8
    Expert éminent sénior

    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2010
    Messages
    5 380
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2010
    Messages : 5 380
    Points : 10 410
    Points
    10 410
    Par défaut
    Citation Envoyé par dancom5 Voir le message
    je me base toujours en premier sur php.net
    Pas assez, tu n'as pas compris les requêtes préparées. Elles s'appuient sur des marqueurs nommés ou des marqueurs interrogatifs et il n'y en a pas dans tes requêtes, c'est ce que t'a dit rawsrc. Et sabotage disait qu'il ne faut pas toujours faire des requêtes préparées car c'est inutile et je t'ai donné un exemple dans mon dernier message.

  9. #9
    Membre actif Avatar de dancom5
    Homme Profil pro
    Inscrit en
    Janvier 2010
    Messages
    808
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55

    Informations forums :
    Inscription : Janvier 2010
    Messages : 808
    Points : 241
    Points
    241
    Par défaut
    Je pense que c'est bon comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $query_pag_data = "SELECT * from crud_categorie LIMIT $start, $per_page";
     
    $msg = "";
     
    foreach ($con->query($query_pag_data) as $row) {
        $msg .= "<li><b>" . $row['id'] . "</b> " . $row['categorie'] . "</li>";
    }
    $msg = "<div class='data'><ul>" . $msg . "</ul></div>"; // Content for Data
     
    /* --------------------------------------------- */
     
    $query_pag_num = "SELECT count(*) FROM crud_categorie";
    $result_pag_num = $con->query($query_pag_num);
    $count = $result_pag_num->fetchColumn();
    On oublie souvent la simplicité ou la base dans la vie: Maslow

  10. #10
    Expert éminent sénior

    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2010
    Messages
    5 380
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2010
    Messages : 5 380
    Points : 10 410
    Points
    10 410
    Par défaut
    Oui tant que les variables $start et $per_page ne sont pas des variables utilisateur (sinon vaudrait mieux les protéger ou les caster préalablement en entiers).

    Au passage si ça t'intéresse y'a un topic ici assez documenté sur un sujet très similaire au tiens (migration de mysql vers pdo). Le 11ème message de ce topic pourrait t'intéresser notamment si tu avais à faire des requêtes préparées avec des paramètres utilisateurs dans la clause limit.

  11. #11
    Membre actif Avatar de dancom5
    Homme Profil pro
    Inscrit en
    Janvier 2010
    Messages
    808
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55

    Informations forums :
    Inscription : Janvier 2010
    Messages : 808
    Points : 241
    Points
    241
    Par défaut
    C'est en parti utilisateur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $page = $_POST['page'];
    $cur_page = $page;
    $page -= 1;
    $per_page = 1;
    $previous_btn = true;
    $next_btn = true;
    $first_btn = true;
    $last_btn = true;
    $start = $page * $per_page;
    $start est en parti utilisateur à cause de $page.

    Merci pour la suggestion du topic ABCIWEB, je vais voir.
    Merci aussi pour vos intervention rawsrc et sabotage.
    On oublie souvent la simplicité ou la base dans la vie: Maslow

  12. #12
    Expert éminent sénior

    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2010
    Messages
    5 380
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2010
    Messages : 5 380
    Points : 10 410
    Points
    10 410
    Par défaut
    Oui ben dans ces cas là si tu attends un entier dans $_POST['page'] tu peux simplement le caster en entier et lui donner une valeur par défaut si la valeur fournie n'est pas numérique donc je ferais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $page = isset($_POST['page']) && is_numeric($_POST['page']) ? intval($_POST['page']) : 0 ;
    avec ça $page est suffisamment contrôlée.

  13. #13
    Membre actif Avatar de dancom5
    Homme Profil pro
    Inscrit en
    Janvier 2010
    Messages
    808
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55

    Informations forums :
    Inscription : Janvier 2010
    Messages : 808
    Points : 241
    Points
    241
    Par défaut
    Merci

    Et dans le cas, où j'aurais un second paramétré comme ceci :

    On oublie souvent la simplicité ou la base dans la vie: Maslow

  14. #14
    Expert éminent sénior

    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2010
    Messages
    5 380
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2010
    Messages : 5 380
    Points : 10 410
    Points
    10 410
    Par défaut
    Ben là apparemment c'est des variables GET et si tu attends des entiers tu peux faire le même principe que pour l'exemple avec POST['page']. Où vois-tu un problème ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $page = isset($_GET['page']) && is_numeric($_GET['page']) ? intval($_GET['page']) : 0 ;
    $section = isset($_GET['section']) && is_numeric($_GET['section']) ? intval($_GET['section']) : 0 ;
    (faire attention quand même que la valeur 0 par défaut soit correcte)
    C'était pas bien difficile à trouver, faudrait que tu bosse un peu ...

    Après si tu veux utiliser les dernières fonctions à la mode tu peux utiliser filter_input mais dans ce cas de figure tu n'y gagnera rien.

  15. #15
    Membre actif Avatar de dancom5
    Homme Profil pro
    Inscrit en
    Janvier 2010
    Messages
    808
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55

    Informations forums :
    Inscription : Janvier 2010
    Messages : 808
    Points : 241
    Points
    241
    Par défaut
    Merci pour l'aide.

    J'ai approfondie mes connaissances générale en PDO.

    C'est vrai que c'est plutôt agréable son utilisation.
    On oublie souvent la simplicité ou la base dans la vie: Maslow

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 4
    Dernier message: 06/06/2004, 12h07
  2. [QuickReport] convertir les états en RAVE
    Par El blérot dans le forum Rave
    Réponses: 5
    Dernier message: 18/03/2004, 17h05
  3. Réponses: 3
    Dernier message: 02/07/2003, 16h24
  4. [TP]Convertir Delphi en BP7
    Par Christophe Fantoni dans le forum Turbo Pascal
    Réponses: 28
    Dernier message: 19/11/2002, 17h30
  5. convertir un nom long (win32) en format dos (8+3)
    Par kylekiller dans le forum Langage
    Réponses: 2
    Dernier message: 30/08/2002, 13h34

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo