Comment insérer des valeurs NULL en utilisant PDO?

105

J'utilise ce code et je suis au-delà de la frustration:

try {
    $dbh = new PDO('mysql:dbname=' . DB . ';host=' . HOST, USER, PASS);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $dbh->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
}
catch(PDOException $e)
{
    ...
}
$stmt = $dbh->prepare('INSERT INTO table(v1, v2, ...) VALUES(:v1, :v2, ...)');
$stmt->bindParam(':v1', PDO::PARAM_NULL); // --> Here's the problem

PDO::PARAM_NULL, null, '', tous échouent et lancent cette erreur:

Erreur fatale : impossible de passer le paramètre 2 par référence dans / opt / ...

Nacho
la source

Réponses:

138

J'apprends juste l'AOP, mais je pense que vous devez utiliser bindValue, pasbindParam

bindParamprend une variable, comme référence, et ne tire pas de valeur au moment de l'appel bindParam. J'ai trouvé ceci dans un commentaire sur la documentation php:

bindValue(':param', null, PDO::PARAM_INT);

EDIT: PS Vous pourriez être tenté de le faire bindValue(':param', null, PDO::PARAM_NULL);mais cela n'a pas fonctionné pour tout le monde (merci Will Shaver pour le rapport.)

JasonWoof
la source
Je ne suis pas sûr de la différence entre les deux, mais je vais en étudier quelques-uns. Merci, votre réponse était également excellente.
Nacho
3
Je pense que cela pourrait être une meilleure réponse que la mienne (si cela fonctionne vraiment)
Joe Phillips
2
J'ai eu des problèmes avec PDO :: PARAM_NULL sur MySql 5.1.53, mais PDO :: PARAM_INT avec une valeur nulle fonctionnait très bien.
Will Shaver
2
Odin: Je pense que le plus important est de passer les trois paramètres :). dans la question d'ign, il passait PDO :: PARAM_NULL comme valeur, au lieu de comme type (et ne transmettait pas du tout un type.)
JasonWoof
1
FYI: dans la plupart des cas, vous êtes tout à fait d'accord avec l'utilisation de bindValue()over bindParam(); pour tout, pas seulement pour la NULLvaleur. Je ne peux pas penser à un seul cas où vous auriez besoin de lier le paramètre à une référence d'une variable PHP - mais c'est ce que bindParam()fait.
low_rents
48

Lors de l'utilisation, bindParam()vous devez passer une variable, pas une constante. Donc, avant cette ligne, vous devez créer une variable et la définir surnull

$myNull = null;
$stmt->bindParam(':v1', $myNull, PDO::PARAM_NULL);

Vous obtiendrez le même message d'erreur si vous essayiez:

$stmt->bindParam(':v1', 5, PDO::PARAM_NULL);
Joe Phillips
la source
Vous avez bien compris, je viens de réaliser que je l'avais déjà fait dans mon code, $ null = PDO :: PARAM_NULL; Merci.
Nacho
1
Il est préférable d'utiliser bindValue () dans ce cas, si vous allez quand même créer des espaces réservés. bindParam () est initialement destiné à exécuter une requête, puis à modifier les variables et à réexécuter sans lier à nouveau les paramètres. bindValue () se lie immédiatement, bindParam () uniquement lors de l'exécution.
Hugo Zink
1
J'ai trouvé que null doit être en minuscules dans la ligne $ myNull = null. $ myNull = NULL n'a PAS fonctionné.
raphael75
28

Lors de l'utilisation de INTEGERcolonnes (cela peut être NULL) dans MySQL, PDO a un comportement (pour moi) inattendu.

Si vous utilisez $stmt->execute(Array), vous devez spécifier le littéral NULLet ne pouvez pas donner NULLpar référence de variable. Donc cela ne fonctionnera pas:

// $val is sometimes null, but sometimes an integer
$stmt->execute(array(
    ':param' => $val
));
// will cause the error 'incorrect integer value' when $val == null

Mais cela fonctionnera:

// $val again is sometimes null, but sometimes an integer
$stmt->execute(array(
    ':param' => isset($val) ? $val : null
));
// no errors, inserts NULL when $val == null, inserts the integer otherwise

J'ai essayé ceci sur MySQL 5.5.15 avec PHP 5.4.1

ChrisF
la source
1
$ stmt-> execute (array (': param' =>! vide ($ val)? $ val: null)); Utilisez! Vide car pour une chaîne vide, vous voudrez également définir null dans la base de données
Shehzad Nizamani
1
@ShehzadNizamani Uniquement si le type de colonne est entier, comme dans son exemple, oui. Mais autrement isset()correspond mieux à NULL. Une chaîne vide est également une valeur.
StanE du
8

Pour ceux qui ont encore des problèmes (Impossible de passer le paramètre 2 par référence), définissez une variable avec une valeur nulle, et pas simplement passer null à PDO:

bindValue(':param', $n = null, PDO::PARAM_INT);

J'espère que cela t'aides.

Pedro Guglielmo
la source
6

J'ai eu le même problème et j'ai trouvé cette solution fonctionnant avec bindParam:

    bindParam(':param', $myvar = NULL, PDO::PARAM_INT);
user1719210
la source
3

Si vous souhaitez insérer NULLuniquement lorsque le valueest emptyou '', mais insérez le valuelorsqu'il est disponible.

A) Reçoit les données du formulaire à l'aide de la méthode POST et appelle l'insertion de fonction avec ces valeurs.

insert( $_POST['productId'], // Will be set to NULL if empty    
        $_POST['productName'] ); // Will be to NULL if empty                                

B) évalue si un champ n'a pas été rempli par l'utilisateur et insère NULLsi c'est le cas.

public function insert( $productId, $productName )
{ 
    $sql = "INSERT INTO products (  productId, productName ) 
                VALUES ( :productId, :productName )";

    //IMPORTANT: Repace $db with your PDO instance
    $query = $db->prepare($sql); 

    //Works with INT, FLOAT, ETC.
    $query->bindValue(':productId',  !empty($productId)   ? $productId   : NULL, PDO::PARAM_INT); 

    //Works with strings.
    $query->bindValue(':productName',!empty($productName) ? $productName : NULL, PDO::PARAM_STR);   

    $query->execute();      
}

Par exemple, si l'utilisateur ne saisit rien dans le productNamechamp du formulaire, alors ce $productNamesera SETmais EMPTY. Donc, vous devez vérifier si c'est le empty()cas et si c'est le cas, insérez-le NULL.

Testé sur PHP 5.5.17

Bonne chance,

Arian Acosta
la source
3
Ou, vous pouvez utiliser la IFNULL()fonction de MySQL dans la chaîne de requête. Comme ça:$sql = "INSERT INTO products ( productId, productName ), VALUES ( IFNULL(:productId, NULL), IFNULL(:productName, NULL) )";
starleaf1
J'aime la propreté de votre solution. Je viens de le tester, mais malheureusement, il insère des chaînes vides au lieu de NULL lorsque l'utilisateur n'écrit rien sur l'entrée. C'est juste une question de préférence, mais j'ai plutôt des NULL au lieu de chaînes vides.
Arian Acosta
0

Essaye ça.

$stmt->bindValue(':v1', null, PDO::PARAM_NULL); // --> insert null
Hector Teran
la source
1
Avant d'essayer de répondre à une question aussi ancienne, vous devez d'abord lire les autres réponses. Peut-être a-t-on déjà répondu.
Votre bon sens
0

Sur la base des autres réponses mais avec un peu plus de clarté sur la manière d'utiliser réellement cette solution.

Si, par exemple, vous avez une chaîne vide pour une valeur de temps mais que vous souhaitez l'enregistrer sous forme de null:

  if($endtime == ""){
    $db->bind(":endtime",$endtime=NULL,PDO::PARAM_STR);
  }else{
    $db->bind("endtime",$endtime);
  }

Notez que pour les valeurs de temps, vous utiliserez PARAM_STR, car les heures sont stockées sous forme de chaînes.

Vincent
la source
-1

Dans mon cas, j'utilise:

  • SQLite,

  • des instructions préparées avec des espaces réservés pour gérer un nombre inconnu de champs,

  • Demande AJAX envoyée par l'utilisateur où tout est une chaîne et il n'y a rien de tel que NULLvaleur et

  • J'ai désespérément besoin d'insérer des NULLs car cela ne viole pas les contraintes de clé étrangère (valeur acceptable).

Supposons que maintenant l'utilisateur envoie avec post: $_POST[field1]avec une valeur value1qui peut être la chaîne vide ""ou "null"ou "NULL".

Je fais d'abord la déclaration:

$stmt = $this->dbh->prepare("INSERT INTO $table ({$sColumns}) VALUES ({$sValues})");

{$sColumns}est qc comme field1, field2, ...et {$sValues}sont mes espaces réservés ?, ?, ....

Ensuite, je collecte mes $_POSTdonnées liées aux noms de colonnes dans un tableau $values et les remplace par NULLs:

  for($i = 0; $i < \count($values); $i++)
     if((\strtolower($values[$i]) == 'null') || ($values[$i] == ''))
        $values[$i] = null;

Maintenant, je peux exécuter:

$stmt->execute($values);

et entre autres contourner les contraintes de clé étrangère.

Si par contre, une chaîne vide a plus de sens, vous devez vérifier si ce champ fait partie d'une clé étrangère ou non (plus compliqué).

centurien
la source