2017-07-26 3 views
0

J'analyse un fichier XML et je voudrais INSERT les nœuds éléments XML si id n'existe pas, ou Mise à jour dossier si elle existe ...parse XML et insérer DB si EXISTE METTRE À JOUR sinon

Voici mon code à ce jour:

<?php 
header('Content-type: text/html; charset=UTF-8') ; 
//connection to DB here.. 
//.... .. .. .. .. 


// Create connection 
$conn = new mysqli($servername, $dbuser, $password, $dbname,3306); 
// Check connection 
if ($conn->connect_error) { 
    die("Connection failed: " . $conn->connect_error); 
} 
// Change character set to utf8 
$conn->set_charset("utf8"); 
date_default_timezone_set('Europe/Athens'); 

$date_modified = strtotime("now"); 
$business_id = 54; 
$xml_link = "https://www.mydomain.gr/test.xml"; 
$xml_link = $conn->real_escape_string($xml_link); 
$xml_link = trim(stripslashes($xml_link)); 

ici, je mets à jour l'enregistrement "DATE_MODIFIED" ..

$update_business_xml = $conn->query('UPDATE business_xml SET date_modified="' . $date_modified . '" WHERE business_id=54'); 

Maintenant, dans cette section de code, je reçois tous les IDs des produits, et les mettre dans un tableau

$count_errors = 0; 
//query to find products ids 
$query_ids = $conn->query('SELECT pid FROM products WHERE business_id=54'); 
$rows_ids = mysqli_num_rows($query_ids); 
$count_id = 0; 
if($rows_ids > 0) { 
    while($exe_ids = mysqli_fetch_object($query_ids)) { 
     $arr_ids[$count_id] = $exe_ids->pid; 
     $count_id++; 
    } 
} 

$reader = new XMLReader(); 
$reader->open($xml_link);   
while($reader->read()) { 
    if($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'product') { 
     $product = new SimpleXMLElement($reader->readOuterXml()); 

     $pid = $product->id; 
     $name = $product->name; 
     $name = mb_strtolower($name,'UTF-8'); 
     $mpn = $product->mpn; 
     $ean = $product->ean; 
     $sku = $product->sku; 
     $link = $product->link; 
     $price = $product->price; 
     $category_id = $product->category->attributes(); 
     $category_path = $product->category; 
     $category_path = mb_strtolower($category_path,'UTF-8'); 
     $image = $product->image; 
     $availability = $product->availability; 
     $size = $product->size; 
     $size = mb_strtolower($size,'UTF-8'); 
     $color = $product->color; 
     $color = mb_strtolower($color,'UTF-8'); 
     $weight = $product->weight; 
     $description = $product->description; 
     $manufacturer = $product->manufacturer; 
     $manufacturer = trim($manufacturer); 
     $instock = "Y"; 
     $product_image = $image; 
     $check_product_url = $link; 

     $exist_pids = 0; 
     if(!empty($pid) || !empty($image) || !empty($price) || !empty($name) || !empty($link) || !empty($manufacturer)) { 
      if($category_id == 613 || $category_id == 604 || $category_id == 635) { 

J'ai quelques catégories en XML que je ne veux pas dans mon DB, alors voici je Je reçois l'attribut de la catégorie, et vérifie si ce produit est l'un d'entre eux, il suffit de mettre à jour le statut de cet enregistrement sur ma base de données.

   $update_business_xml = $conn->query('UPDATE products SET status=0 WHERE business_id="' . $business_id . '" AND pid= "' . $pid . '"'); 
       $count_errors++; 
      } 
      else { 
       $status = 1; 
       $date = date('d-m-Y H:i:s'); //when insert a pr 
       $date_modified = strtotime("now"); //when modify a pr 
       $insert_business_xml = $conn->query('INSERT INTO products (business_id,pid,name,category,product_link,price,size,color,weight,description,manufacturer,mpn,ean,image,sku,instock,availability,status,date_added) VALUES("'.$business_id.'", 
       "' . mysqli_real_escape_string($conn,stripslashes($pid)) . '", 
       "' . mysqli_real_escape_string($conn,stripslashes($name)) . '", 
       "' . mysqli_real_escape_string($conn,stripslashes(trim($category_path))) . '", 
       "' . $check_product_url . '", 
       "' . mysqli_real_escape_string($conn,stripslashes($price)) . '", 
       "' . mysqli_real_escape_string($conn,stripslashes(trim(strtolower($size)))) . '", 
       "' . mysqli_real_escape_string($conn,stripslashes(trim(strtolower($color)))) . '", 
       "' . mysqli_real_escape_string($conn,stripslashes(trim($weight))) . '", 
       "' . mysqli_real_escape_string($conn,stripslashes(trim($description))) . '", 
       "' . mysqli_real_escape_string($conn,stripslashes(trim(strtolower($manufacturer)))) . '", 
       "' . mysqli_real_escape_string($conn,stripslashes(trim($mpn))) . '", 
       "' . mysqli_real_escape_string($conn,stripslashes(trim($ean))) . '", 
       "' . $product_image . '", 
       "' . mysqli_real_escape_string($conn,stripslashes(trim($sku))) . '", 
       "' . mysqli_real_escape_string($conn,stripslashes($instock)) . '", 
       "' . mysqli_real_escape_string($conn,stripslashes($availability)) . '", 
       "' . $status . '", "' . $date . '") ON DUPLICATE KEY UPDATE 
       "business_id='.$business_id.'", 
       "pid=' . mysqli_real_escape_string($conn,stripslashes($pid)) . '", 
       "name=' . mysqli_real_escape_string($conn,stripslashes($name)) . '", 
       "category=' . mysqli_real_escape_string($conn,stripslashes(trim($category_path))) . '", 
       "product_link=' . $check_product_url . '", 
       "price=' . mysqli_real_escape_string($conn,stripslashes($price)) . '", 
       "size=' . mysqli_real_escape_string($conn,stripslashes(trim(strtolower($size)))) . '", 
       "color=' . mysqli_real_escape_string($conn,stripslashes(trim(strtolower($color)))) . '", 
       "weight=' . mysqli_real_escape_string($conn,stripslashes(trim($weight))) . '", 
       "description=' . mysqli_real_escape_string($conn,stripslashes(trim($description))) . '", 
       "manufacturer=' . mysqli_real_escape_string($conn,stripslashes(trim(strtolower($manufacturer)))) . '", 
       "mpn=' . mysqli_real_escape_string($conn,stripslashes(trim($mpn))) . '", 
       "ean=' . mysqli_real_escape_string($conn,stripslashes(trim($ean))) . '", 
       "image=' . $product_image . '", 
       "sku=' . mysqli_real_escape_string($conn,stripslashes(trim($sku))) . '", 
       "instock=' . mysqli_real_escape_string($conn,stripslashes($instock)) . '", 
       "availability=' . mysqli_real_escape_string($conn,stripslashes($availability)) . '", 
       "status=' . $status . '", 
       "date_modified=' . $date_modified . '"'); 

Dans le code ci-dessus, je suis en utilisant l'instruction INSERT INTO ... Duplicate KEY UPDATE afin de vérifier si l'identifiant existe, simplement mettre à jour toutes les valeurs de ce disque .. Si l'ID n'existe pas, INSERT celui-ci .. au beggining de mon code comme je l'ai dit, je suis tous les ids dans un tableau, donc ici, je suis en train d'effacer l'ID qui ont été trouvé ..

   //erase from arr_ids 
       if(($key = array_search($pid, $arr_ids)) !== false) { 
        unset($arr_ids[$key]); 
       } 
      } 
     } 
     else { 
      $update_business_xml = $conn->query('UPDATE products SET status=0, date_modified="' . $date_modified . '" WHERE business_id="' . $business_id . '" AND pid= "' . $pid . '"'); 
      $count_errors++; 
     } 
    } //reader nodeType 
} //end while loop 
$reader->close(); 

Last but not least, ici, je mets aussi à jour l'enregistrement avec tout le reste des identifiants qui ont été laissés à l'intérieur du tableau ... ce qui signifie que ces identifiants, n'existe pas ou ne sont plus listés dans le XML.

foreach($arr_ids as $id) { 
    $update_business_xml = $conn->query('UPDATE products SET status=0 WHERE business_id="' . $business_id . '" AND pid= "' . $id . '"'); 
} 

Enfin, j'ai un message simple afin de voir combien d'erreurs il y a .. En disant des erreurs, je veux dire le nombre d'enregistrements du XML comporte des éléments vides (nom, prix, lien, l'image .. .. etc) et juste faire un écho ..

//var_dump($arr_ids); 
$insert_messages = "Your XML file has been updated successfully! We found <strong>" . $count_errors . "</strong> errors. In case errors found, please check your dashboard!"; 
echo $insert_messages; 
$conn->close(); 
?> 

Tout ce code est dans un fichier php, qui fonctionne comme cronjob! Maintenant le problème et ma question est, que je n'ai pas vu le UPDATE fonctionnant correctement ou pas du tout, parce que le date_modified est toujours NULL dans ma base de données, qui est la valeur d'initialisation. Que suis-je manquant? (Et en général ce que je pouvais faire pour corriger le code tout, s'il y a plus d'une erreur ici?)

merci à l'avance

échantillon XML avec un produit (XML grec)

<mystore> 
<created_at>2017-07-26 16:01:20</created_at> 
<products> 
<product> 
<id>9307</id> 
<name> 
<![CDATA[ Minimum ανδρικό t-shirt φλάμα Percy ivory ]]> 
</name> 
<link> 
<![CDATA[ 
https://www.mydomain.gr/andrika-rouxa/tshirts-andrikes-mployzes/minimum-andriko-t-shirt-percy-ivory.html 
]]> 
</link> 
<image> 
<![CDATA[ 
https://www.mydomain.gr/images/detailed/51/minimum-andriko-t-shirt-percy-122690105_(1).jpg 
]]> 
</image> 
<sku> 
<![CDATA[ 122690105-wh ]]> 
</sku> 
<mpn> 
<![CDATA[ 122690105-wh ]]> 
</mpn> 
<category id="30"> 
<![CDATA[ ΑΝΔΡΙΚΑ > T- shirts ]]> 
</category> 
<price>27.30</price> 
<description> 
<![CDATA[ 
<ul><li>χρώμα ελεφαντόδοντου</li><li>στρογγυλή λαιμόκοψη</li><li>στρογγυλεμένο και μακρύτερο πίσω μέρος</li><li>regular fit</li><li>100% cotton</li></ul> 
]]> 
</description> 
<instock>Y</instock> 
<availability>Σε απόθεμα</availability> 
<manufacturer> 
<![CDATA[ Minimum ]]> 
</manufacturer> 
<size>L,XL</size> 
<sex> 
<![CDATA[ Άνδρας ]]> 
</sex> 
</product> 
</products> 
</mystore> 

date_modified est varchar (128) dans mon DB - utf8_general_ci Je ne pense pas que ce soit un problème, non?

+0

@Parfait J'ai éditer ma question .. merci de prendre le temps de répondre .. –

+0

Quel 'UPDATE' ne fonctionne pas? La toute première boucle avant ou celle avec 'INSERT'? S'il vous plaît expliquer ce qui fonctionne. Les enregistrements sont-ils ajoutés et mis à jour sauf pour * date_modified *? Si ce champ est un 'varchar' comme vous le prétendez, la première mise à jour ne pourrait pas échouer même si vous passez un entier,' strtotime() 'car MySQL lancera en conséquence. – Parfait

+0

Notez que votre 'INSERT' ne touche pas * date_modified *, seulement * date_added *. Donc, ces enregistrements seraient NULL pour * date_ modified *. Vérifiez soigneusement vos données et signalez un problème spécifique. – Parfait

Répondre

1

Envisagez d'utiliser un tableau products_temp, structure exacte de produits mais uniquement utilisé pour stocker des données XML. À partir de là, exécutez les requêtes d'ajout et de mise à jour nécessaires pour migrer de temp vers la table finale.

Et définitivement s'il y a des plats à emporter ici, utilisez parameterized queries qui évite les enclos de devis et la concaténation variable pour un code plus propre, plus sûr et plus facile à entretenir.

données XML Manipulation (append requête à l'intérieur boucle)

// CLEAN OUT TEMP TABLE 
$sql = 'DELETE FROM products_temp'; 
$delete_xml = $conn->query($sql); 

// PREPARED STATEMENT 
$sql = 'INSERT INTO products_temp (business_id, pid, `name`, `category`, product_link, price, 
            size, color, weight, `description`, manufacturer, mpn, ean, 
            image, sku, instock, availability, `status`, date_added) 
     VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)'; 

// APPEND ALL RAW XML DATA INTO TEMP TABLE (IN LOOP, WITHOUT INNER IF LOGIC) 
//...same xml objects 

while($reader->read()) { 
    if($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'product') { 
     //...same xml variables 
     $product = new SimpleXMLElement($reader->readOuterXml()); 
     $stmt = $conn->prepare($sql); 

     $stmt->bind_param("sssssssssssssssssss", 
          mysqli_real_escape_string($conn,stripslashes($pid)), 
          mysqli_real_escape_string($conn,stripslashes($name)), 
          mysqli_real_escape_string($conn,stripslashes(trim($category_path))), 
          $check_product_url, 
          mysqli_real_escape_string($conn,stripslashes($price)), 
          mysqli_real_escape_string($conn,stripslashes(trim(strtolower($size)))), 
          mysqli_real_escape_string($conn,stripslashes(trim(strtolower($color)))), 
          mysqli_real_escape_string($conn,stripslashes(trim($weight))), 
          mysqli_real_escape_string($conn,stripslashes(trim($description))), 
          mysqli_real_escape_string($conn,stripslashes(trim(strtolower($manufacturer)))), 
          mysqli_real_escape_string($conn,stripslashes(trim($mpn))), 
          mysqli_real_escape_string($conn,stripslashes(trim($ean))), 
          $product_image, 
          mysqli_real_escape_string($conn,stripslashes(trim($sku))), 
          mysqli_real_escape_string($conn,stripslashes($instock)), 
          mysqli_real_escape_string($conn,stripslashes($availability)), 
          $status, 
          $date); 

     $stmt->execute(); 
    } 
} 

Data Temp manipulation (boucle extérieure, chaque appelé une fois, évite l'utilisation de tableaux et autres foreach)

Utilisation du WHERE NOT EXISTS

// APPEND ONLY NEW TEMP PRODUCTS WITH RELEVANT INFO AND NOT IN SPECIAL CATEGS INTO PRODUCTS 
$sql = 'INSERT INTO products (business_id, pid, `name`, `categor`y, product_link, price, 
           size, color, weight, `description`, manufacturer, mpn, ean, 
           image, sku, instock, availability, `status`, date_added) 
     SELECT t.business_id, t.pid, t.name, t.category, t.product_link, t.price, 
       t.size, t.color, t.weight, t.description, t.manufacturer, t.mpn, t.ean, 
       t.image, t.sku, t.instock, t.availability, t.status, t.date_added 
     FROM products_temp t 
     WHERE NOT EXISTS (SELECT 1 FROM products sub 
          WHERE sub.p_id = t.p_id AND sub.business_id = t.business_id) 
      AND t.image IS NOT NULL AND t.price IS NOT NULL AND t.name IS NOT NULL 
      AND t.link IS NOT NULL AND t.manufacturer IS NOT NULL 
      AND t.category_id NOT IN (604, 613, 635)'; 
$insert_business_xml = $conn->query($sql); 

En utilisant UPDATE INNER JOIN

// UPDATE MATCHED TEMP PROUCTS WITH MISSING RELEVANT INFO OR IN SPECIAL CATEGS (I.E., ERRORS) 
$sql = 'UPDATE products p INNER JOIN products_temp t 
          ON p.p_id = t.p_id AND p.business_id = t.business_id 
     SET p.status=0, p.date_modified = ? 
     WHERE t.image IS NULL OR t.price IS NULL OR t.name IS NULL 
      OR t.link IS NULL OR t.manufacturer IS NULL 
      OR t.category IN (604, 613, 635)'; 

$stmt = $conn->prepare($sql); 
$stmt->bind_param("s", $date_modified); 

$stmt->execute(); 
$count_errors = $mysqli->affected_rows;  // ERRORS FOR MESSAGE AT END 


// UPDATE EXISTING MATCHED TEMP PRODUCTS WITH RELEVANT INFO AND NOT IN SPECIAL CATEGS 
$sql = 'UPDATE products p INNER JOIN products_temp t 
          ON p.p_id = t.p_id AND p.business_id = t.business_id 
     SET p.business_id = t.business_id, p.name = t.name, p.category = t.category, 
      p.product_link = t.product_link, p.price = t.price, p.size = t.size, 
      p.color = t.color, p.weight = t.weight, p.description = t.description, 
      p.manufacturer = t.manufacturer, p.mpne = t.mpn, p.ean = t.ean, 
      p.image = t.image, p.sku = t.sku, p.instock = t.instock, 
      p.availability = t.availability, p.status = t.status, p.date_added = t.date_added 
     WHERE t.image IS NOT NULL AND t.price IS NOT NULL AND t.name IS NOT NULL 
      AND t.link IS NOT NULL AND t.manufacturer IS NOT NULL 
      AND t.category_id NOT IN (604, 613, 635)'; 
$update_business_xml = $conn->query($sql); 


// UPDATE EXISTING NON-MATCHED TEMP PRODUCTS 
$sql = 'UPDATE products p SET p.status = 0 
     WHERE NOT EXISTS (SELECT 1 FROM products_temp sub 
          WHERE sub.p_id = p.p_id AND sub.business_id = p.business_id)'; 
$update_business_xml = $conn->query($sql); 

Note: Bien sûr, sans données réelles et base de données tout cela est non testé. S'il vous plaît ajuster toute syntaxe négligée et intégrer dans une base de code plus grande.

+0

Je pense que j'ai compris ce que prépare et "ssssss" fonctionne .. Vous utilisez des questionnaires ..?,?,? ... dans les requêtes mysql et après vous utilisez prepare -> ($ sql) et avec bind_param ("s ", $ date_modified); vous êtes comme remplacer le s avec la variable, non? Mais encore quelques questions si ... Je reçois cette erreur: "_italic_ Nombre d'éléments dans la chaîne de définition de type ne correspond pas au nombre de variables de liaison _italic_" dans cette ligne de code que vous dites: $ stmt = $ conn- > préparer ($ sql); $ stmt-> bind_param ("s", $ date_modified); $ stmt-> execute(); $ count_errors = $ mysqli-> affected_rows; –

+0

si j'en ai un "s" et un "?" à l'intérieur de mysql je devrais avoir une variable ($ date_modified), non? –

+0

Correct. S'il vous plaît lire la pléthore de documents hors ligne/en ligne et des tutoriels sur le paramétrage php/mysqli. Yes '?' Est l'espace réservé et 's' définit le type * string * (utilisez' i' pour les types * integer *). En ce qui concerne l'erreur, vous avez 19 valeurs qui sont passées dans la requête Ajout et j'en ai compté 17 alors j'ai ajouté deux autres points d'interrogation et s. – Parfait