WN

WN (https://www.wn.se/forum/index.php)
-   Serversidans teknologier (https://www.wn.se/forum/forumdisplay.php?f=4)
-   -   uppdatera databas med php och javascript (https://www.wn.se/forum/showthread.php?t=1055287)

jonssondesign 2012-10-10 04:09

uppdatera databas med php och javascript
 
Hejsan, försöker skriva om lite kod på reaplagg.se, bland annat uppladdningen av xml filer (produktlista).

Jag kan verkligen ingenting om javascript, och jag är nybörjare när det gäller php.

Får det hela inte att fungera. Inget händer lixom. Databasen uppdateras inte, men inga felmeddelanden syns heller.
Fälten i databasen blir tomma, om man anger vissa kolumner manuellt vill säga..

Såhär har jag skrivit i ena filen (där man väljer att ladda upp xml fil mm).
PHP-kod:

<?php include('includes/header.php');
if (
checkAccess() == false) {header("Location: login.php");}; /* om man är inte inloggad */
?>
<div id="admin-menu">
    <div id="menu">
        <ul>
            <li class="hem"><a href="/admin/">Hem</a></li>
            <li class="produkter current"><a href="produkter.php">Produkter</a></li>
            <li class="reklam"><a href="ads.php">Reklam</a></li>
            <li class="sidor"><a href="sidor.php">Sidor</a></li>
        </ul>
    </div><!-- menu -->
<div class="clear"></div>
</div>

<?php
if (isset($_POST['submit'])) {
    
$temp_path $_FILES["file"]["tmp_name"];
?>
    <script>
        function displayResult() {
        var path = document.getElementById("path").value;
        var string = "path=" + path;
                
        var xmlhttp;
        if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp=new XMLHttpRequest();
        } else {// code for IE6, IE5
            xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
        }
                
        xmlhttp.open("POST","test.php",true);
        xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
        xmlhttp.send(string);
                
    };
            
    </script>
<?php
};
?>

<div class="clear"></div>
<h2>Lägg till en XML-produktlista</h2>

<form action="" method="post" enctype="multipart/form-data">
    Välj en xml fil: <input type="hidden" name="path" value="<?php if (isset($temp_path)) {echo $temp_path;}; ?>" id="path" />
    <input type="file" name="file" id="fname" />
    <input type="submit" onclick="displayResult()" value="Dubbelklicka för att ladda upp" name="submit" id="submit" />
</form>

<div class="clear"></div>
<?php include('includes/footer.php'); ?>

I test.php har jag sedan detta:
PHP-kod:

$path $_POST['path'];
$xml simplexml_load_file($path); 

Tänker mest på att felet ligger i test.php. Går det verkligen att göra på det där viset tro?
Jag har suttit och fulskrivit js koden för att få fram den riktiga adressen till filen, som jag sedan (tror jag?) skickar med i en POST till test.php och hämtar sedan upp den där. Dock så är det i en ren string, kanske det som ställer till det?

Någon som ser något fel, eller vet en lösning? eller kanske en annan version av en lösning? :)

Tack på förhand!

gregoff 2012-10-10 08:17

Nu har jag inte testat koden så jag kan inte kolla om JavaScript-koden fungerar, men din test.php gör ingenting, egentligen.

Den hämtar en xml-fil och lagrar den i en variabel. Därefter händer ingenting. Inget som skriver till någon databas någonstans (eller ens några funktioner för detta). Det enda simplexml_load_file gör är att läsa in en xml-fil och lagra in den i en variabel.

Jonas 2012-10-10 13:05

Du kan inte ladda upp filer med AJAX.

Dock så skulle ditt ajax anrop inte göra något unikt utan snarare göra något "simpelt" mer komplicerat genom att det du kan göra inom IF-satsen gör du i en extern fil med ajax.

1) Dessutom skickar du inte med sökvägen via ajax-anropet.
Skulle du mot all förmodan göra det så skulle jag ställa mig med den röda flaggan och skrika säkerhetshål och blåsa i visselpipan.

2) Du kan inte hämta ut sökvägen i en <input type="file"> eftersom den pekar lokalt på datorn, om du kunde detta skulle du då skicka en lokalsökväg som inte finns åtkomlig till serversidan.

Bättre och trevligare lösning:
PHP-kod:

<?php
if (isset($_POST['submit'])) {
    
$xml simplexml_load_file($_FILES["file"]["tmp_name"]);
... 
hantera $xml ...
}
?>

Detta är så oerhört mycket smidigare än att blanda in ajax.

jonssondesign 2012-10-10 13:51

Tack för era bådas kommentarer!

Såhär ligger det till:

Gregoff: Jag har för mycket kod i test.php för att vilja skriva ut allt. Men jag vet att detta fungerar, eftersom att jag har gjort precis som Jonas skrev.

Jonas: Jag har gjort precis som du skrev tidigare, så jag vet att det är mycket enklare.:)

Problemet och skälet varför jag vill använda mig av ajax är för att det tar för lång tid att ladda upp produkterna, vilket gör att, på vissa webbhotell (city network), så får jag en 505 error. (jag får inte det på binero).

Jag kan visa hur jag har gjort tidigare:
PHP-kod:

<?php include('includes/header.php');
if (
checkAccess() == false) {header("Location: login.php");}; /* om man är inte inloggad */
?>
    <div id="admin-menu">
        <div id="menu">
            <ul>
            <li class="hem"><a href="/admin/">Hem</a></li>
            <li class="produkter current"><a href="produkter.php">Produkter</a></li>
            <li class="reklam"><a href="ads.php">Reklam</a></li>
            <li class="sidor"><a href="sidor.php">Sidor</a></li>
            </ul>
        </div><!-- menu -->
        <div class="clear"></div>
    </div>
<?php
if (isset($_POST['submit'])) { // när man klickar på OK knappen
    
if (!empty($_FILES["file"]["name"])) {
        if (!empty(
$_POST['store'])) {
            
$allowedExts = array("xml");
            
$array explode("."$_FILES["file"]["name"]);
            
$extension $array['1'];
            if ((
$_FILES["file"]["type"] == "text/xml") && in_array($extension$allowedExts)) {
                if (
$_FILES["file"]["error"] <= 0) {
                    
$filed_store $_POST['store']; // Hämtar datan ur radio-knapparna
                    
if ($filed_store == "nelly") { // om man valt nelly
                        
include('xml_upload_nelly.php');
                    } elseif (
$filed_store == "ellos") { // om man valt ellos
                        
include('xml_upload_ellos.php');
                    } else {
$message "Det verkar som om du har valt <strong>fel butik till xml filen</strong>. Kontrollera en extra gång så att xml filen tillhör " $filed_store ".";};
                } else { 
$message "Error: " $_FILES["file"]["error"]; };
            } else { 
$message "Det verkar som om filen du försöker använda inte är en xml fil."; };
        } else { 
$message "Du måste välja någon av de ovanstående butikerna."; };// om någon av radio knapparna är ifyllda
    
} else { $message "Du måste välja en xml fil att ladda upp."; };
};



?>
<div class="clear"></div>
<h2>Lägg till en XML-produktlista</h2>
<div id="progress"></div>
<form action="" method="post" enctype="multipart/form-data"  id="xml_form">
    <label for="file" id="for_file">Välj xml fil</label> <span id="label_for"></span> <input type="file" name="file" id="file" />
    <div class="clear"></div>
    <div class="clear"></div>
    <div id="xml_stores">
        <div class="xml_box"><input type="radio" name="store" value="nelly" class="xml_store" /> <span>Nelly</span></div>
        <div class="xml_box"><input type="radio" name="store" value="ellos" class="xml_store" /> <span>Ellos</span></div>
    <div class="clear"></div>
    </div>
    <input type="submit" name="submit" value="Ladda upp" id="submit_xml" /> <?php if (isset($message)) {echo "<div class=\"xml_message\">" $message "</div>";}; ?>
</form>

<div class="clear"></div>
<?php include('includes/footer.php'); ?>

xml_upload_nelly.php
PHP-kod:

<?php
                    
$xml_file 
$_FILES["file"]["tmp_name"];
$xml simplexml_load_file($xml_file);

$query "UPDATE products SET fresh = 0";
$update mysql_query($query);

foreach(
$xml->product as $product){
    
set_time_limit(0);
    
$categori $product->fields->productGroup;
    
$gender $product->fields->gender;
    
    if (empty(
$categori) || !isset($categori)) {$categori "-";};
    
    if (
    
$gender == "Kille" || 
    
$gender == "Tjej" || 
    
$gender == "Unisex" || 
    
$gender == "Mamma") {} // om $gender innehåller "kille, tjej, unisex eller mamma", gör inget
    
elseif ($categori == "Baddräkter" || 
    
$categori == "Bikinis" || 
    
$categori == "Blusar & skjortor" || 
    
$categori == "Byxor" || 
    
$categori == "Byxor & shorts" || 
    
$categori == "Festklänningar" || 
    
$categori == "Jackor" || 
    
$categori == "Jeans" || 
    
$categori == "Jumpsuit" || 
    
$categori == "Kjolar" || 
    
$categori == "Klänningar" || 
    
$categori == "Leggings" || 
    
$categori == "Linnen" || 
    
$categori == "Shorts" || 
    
$categori == "Sport bh" || 
    
$categori == "Strandplagg" || 
    
$categori == "T-shirts" || 
    
$categori == "Toppar" || 
    
$categori == "Tröjor" || 
    
$categori == "-")  // om $categori innehåller "något av ovanstående", gör inget
    
{
            if (
$product->fields->sale == "true") {
                
$img mysql_real_escape_string($product->fields->extraImageProductLarge);
                
$title mysql_real_escape_string($product->name); if ($title == "") {$title "-";};
                
$brand mysql_real_escape_string($product->brand); if ($brand == "") {$brand "-";};
                
$store "Nelly";
                
$price mysql_real_escape_string($product->price); if ($price == "") {$price "-";};
                
$url mysql_real_escape_string($product->productUrl);
                
$category mysql_real_escape_string($categori); if ($category == "") {$category "-";};
                
$gender mysql_real_escape_string($product->fields->gender); if ($gender == "") {$gender "-";};
                
$info mysql_real_escape_string($product->description); if ($info == "") {$info "-";};
                
                
$xml_url $product->advertiserProductUrl;
                
$new_xml_url_without substr($xml_url7);
                
$new_xml_url urlencode($new_xml_url_without);
                
$delete_url = array("%2F");
                
$replace_url   = array("/");
                
$new_url_function str_replace($delete_url$replace_url$new_xml_url);
                
                
$advertiserProductUrl "http://" $new_url_function;

                
$query "INSERT INTO products (img, title, brand, store, price, url, category, gender, visible, info, advertiserProductUrl, fresh, new) VALUES ";
                
$query .= "('$img', '$title', '$brand', '$store', $price, '$url', '$category', '$gender', 1, '$info', '$advertiserProductUrl', 1, 1) ";
                
$query .= "ON DUPLICATE KEY UPDATE fresh = 1";
                
$insert mysql_query($query) or die (mysql_error());
            };
    };
};

$query "SELECT id, advertiserProductUrl FROM products WHERE `new` = 1";
$select mysql_query($query) or die(mysql_error());

while (
$result mysql_fetch_array($select)) {
    
set_time_limit(0);
    
$url $result['advertiserProductUrl'];
    
$id $result['id'];
    
$org_price getOrgPrice($url);  

    if (!empty(
$org_price)) {
        
$query "UPDATE `products` SET `org_price`=$org_price,`new`=0 WHERE id = $id";
        
$insert mysql_query($query) or die (mysql_error());
    };
    
};

$query "DELETE FROM products WHERE fresh = 0";
$delete mysql_query($query) or die(mysql_error());

$message "<strong>Super!</strong> Allt fungerade som det skulle, och databasen är nu uppdaterad med nya produkter från <strong>"$filed_store "</strong>.";
                

?>

Detta fungerar kanon, bara att det tar för lång tid. Ibland så överstiger webbläsarens minne, och man får börja om (göra en refresh och skicka med den datan man valt i formuläret igen).

Ibland så tar det för lång tid, så att man får 505 error (händer bara på city network).

Så det jag strävade efter egentligen, var att hitta ett sätt där uppladdningen (xml_upload_nelly.php) sköts bakom stängda dörrar, så att webbläsaren slipper jobba medans koden körs.

Förstod ni? :)

PS. test.php innehöll samma kod som xml_upload_nelly.php innehåller, förutom att $xml_file = $_FILES["file"]["tmp_name"]; byttes ut mot $path = $_POST['path'];

danjel 2012-10-10 17:38

Undvik Ajax för detta som nämnts.
Även om du får det att funka, tänk på att då körs det asynkrona requesten i bakgrunden och kanske timar ut, vilket ju verkar hända i detta fall ibland,
utan att man får reda på att den "failat" eller man kanske stänger ner webbläsaren under tiden.
Men kolla på jquery plugins som har progress indikatorer m.m..om du ändå vill köra ajax

jonssondesign 2012-10-10 20:04

jo precis. Tänkte det jag med.

Men frågan är, hur får jag iväg filen man laddar upp till test.php?

Någon som vet?

Jag vill inte ladda upp själva filen och spara i någon mapp, utan jag använda den webbläsarbaserade versionen av filen helst, så slipper man vänta i typ 5 minuter bara på att den ska laddas upp lixom, och sen läsas av.

Jonas 2012-10-11 00:40

Uppenbarligen så är XML filen stor, du sparar både den och dess objekt i minnet.

Så beroende på hur servern är konfigurerad (memory_limit) så kommer detta att resultera i fel. Likaså timeout på scriptet.

Jag skulle råda dig i att stycka upp filen. Kan vara allt från att du lagrar informationen i omgångar till att köra xx antal rader åt gången.

Testa till exempel att skriva ut data emellanåt samt att se till att variabler återanvänds/NULL:as när dom inte längre används.



Har du tex det så här:
PHP-kod:

$xml simplexml_load_file(...); // 50MB stor fil
$rows $data $xml

Så förbrukar du 150MB. Nu är det lite extremt men vill få fram en poäng.


Titta även på att köra MySQL frågorna med multiple inserts istället en fråga per iteration. Tex.
PHP-kod:

$insert 'INSERT INTO table (field1, field2, field3, ...) VALUES ';
$sql '';
$x 0;
while(...) {
   
$x++;
   ...

   
$sql .= ', (id, value, name, ...)';

   if(
$x == 150) {
      
$query $insert substr($sql1); // Substr tar bort första komma-tecknet.
      
mysql_query($query);
      
$query $sql ''// Nollställ variablar
      
$x 0;
   }
}
// Viktig - lägg in de sista posterna
$query $insert substr($sql1); // Substr tar bort första komma-tecknet.
mysql_query($query); 


jonssondesign 2012-10-11 01:14

Tack! Ska testa lite.

Mina filer är faktiskt på runt 30 - 70mb (produktlistor från nelly och ellos från tradedoubler). Man kan, om man vill, skräddarsy sin produktlista (välja vilka "kolumner?" som ska vara med i xml filen, då kan den blir några mb mindre, men det är inte alltid det går - man kanske vill ladda ner en annans butiks xml fil från annat affiliate sida).

Som sagt, ska kolla lite på detta. Tack! :)

Jonas 2012-10-11 01:21

En annan, mer ren kodoptimering är att köra switch() istället för if().

switch() är "snabbare" än if() i ren optimering, samt gör koden mer läsbar.
"continue" hoppar över hela iterationen och påbörjar nästa istället.

PHP-kod:

switch ($gender) {
    case
"Kille":  
    case 
"Tjej":
    case 
"Unisex":  
    case 
"Mamma"
        continue; 
// om $gender innehåller "kille, tjej, unisex eller mamma", gör inget 
}
    switch (
$categori) {
        case 
"Baddräkter":
...
        case 
"-":  // om $categori innehåller "något av ovanstående", gör inget 
             
continue;



Conny Westh 2012-10-11 04:01

Min fråga är varför du inte bearbetar data lokalt i din lokala databas innan du skickar upp informationen?

Om du gör:

Kod:

INSERT Into NyTabell ( col1, col2, col3... )
SELECT col1, col2, col3...
FROM GammalTabellen
WHERE gender in (select gender from GenderTabellen)

... så kommer du bara få med det data som du vill ha från början och det reducerar mer eller mindre hela behovet av att ha en massa komplex logic i din PHP-kod... Eller är det något jag har missat i kravbilden?

jonssondesign 2012-10-11 04:40

Tack, Jonas!

Conny, hur menar du? Kom ihåg - jag är nybörjare.

Vad menar du med "lokal databas"? xampp på datorn? Hur ska det fungera när man kör live? :P Förstår att jag missförstår dig nu.

Helst skulle jag vilja göra såhär:

Köra allt med php, men efter ett visst antal rader som blivit insatta i databasen, så ska det skrivas ut något - laddar kanske?
Därefter så fortsätter den att ladda upp datan igen.

Men problemet är ju att servern läser filen uppifrån och ned, och utför inget förren allt har bearbetats. Så, om jag skriver echo någonstans i koden, och det finns mer kod nedanför, så uträttas inte det förren hela sidan är färdig, eftersom - vad jag förstår - så bearbetas all php kod i servern, och sen skickar servern en ren html sida till webbläsaren, som sedan visar upp den till mig.
Det vill säga - Allt som finns i koden måste bearbetas, utföras mm, innan servern skickar html koden till webbläsaren. har jag rätt?

Jag vill iaf komma bort från att sidan får error pga. långa laddningstider och mycket minne.

Erik Stenman 2012-10-11 10:15

Något du bör kunna göra är att ändra memory_limit i PHP.
Dessutom kan du ju fundera på att ladda upp filerna med ftp.

danjel 2012-10-11 11:09

30-70 mb, hur många rader data?

Kolla alternativet med FTP som nämnts. Det går att ansluta till ftp servern från php script.
Hursomhelst är det inte lämpligt att använda webbformulär för så stora filer.

Men gör så att du delar upp detta i två delar
1, filuppladdning på något vis till servern, kolla om de t.ex kan ftp:a till en mapp på din server
2, ett php script som hanterar den uppladdade filen,
detta kan mest lämpligen göras så att ett cron jobb körs med jämna mellanrum.

För övrigt så använd transaktioner med databasfrågorna. Annars kan du få korrupt data.

Vad gäller databasfrågorna så misstänker jag de tar rätt lång tid.
Testa att ta tid på de olika frågorna.
Tabelltyp är den MyISAM eller InnoDB?

Ett eventuellt alternativ är att ta bort eventuella index på 'products' tabellen innan du kör alla inserts.
Och sen lägga tillbaka index efter inserts, men det tenderar att bli rätt avancerat.

Kolla om det finns index på dessa kolumner där "where" används:

SELECT id, advertiserProductUrl FROM products WHERE `new` = 1
DELETE FROM products WHERE fresh = 0

jonssondesign 2012-10-11 13:08

Erik: Tack, det vore ju ett alternativ kanske :)

Danjel: Filerna innehåller 10 000+ produkter - ca.15 rader per produkt.

Jag har inga problem hos Binero, där fungerar det både att hämta xml filen via url (ladda upp via ftp, och sen hämta xml adressen), det går även bra att ladda upp via en <input type="file" utan problem.

Men jag har inlett ett samarbete med en kille som har City Network, och där failar den hela tiden - 1) går inte att hämta filer via en url (oavsett om filen ligger på samma server eller inte. 2) tar det för lång tid så dör servern.

"För övrigt så använd transaktioner med databasfrågorna. Annars kan du få korrupt data." - vad menar du med det? :)

Det är av typen MyISAM.

Jag har 3 index typer i tabellen Products.
1) BTREE - ID (you know..)
2) BTREE - URL (för att förhindra dubletter vid input)
3) FULLTEXT - INFO (Fulltext sökningsförmåga)

"Kolla om det finns index på dessa kolumner där "where" används:" - ehm. say what what?
ooh! nej, inget index på varken new eller fresh. as you can se.

Vill tacka alla som försöker hjälpa en krokad person som mig! :)

danjel 2012-10-11 14:48

Citat:

Ursprungligen postat av jonssondesign (Inlägg 20452721)
"För övrigt så använd transaktioner med databasfrågorna. Annars kan du få korrupt data." - vad menar du med det? :)

Ja alltså tänk om din kod smäller eller stoppar av någon anledning t.ex i detta stycke:

$query = "SELECT id, advertiserProductUrl FROM products WHERE `new` = 1";
$select = mysql_query($query) or die(mysql_error());

Då kommer inte dina updates efter det att köra, men dina första inserts har körts och uppdaterat databasen.

Med transaktioner körs allt eller inget , typ denna princip

mysql_query("SET AUTOCOMMIT=0");
mysql_query("START TRANSACTION");

$a1 = mysql_query("INSERT INTO bla (id) VALUES('1')");
$a2 = mysql_query("INSERT INTO blabla(id) VALUES('2')");

if ($a1 and $a2) {
mysql_query("COMMIT"); //allt grönt , skriver ner allt till db
} else {
mysql_query("ROLLBACK"); //backar tillbaks allt efter "START TRANSACTION"
}

Kan inte så mycket om dem i mySQL men du måste nog köra InnoDB dessvärre för att använda transaktioner.
Det brukar rekommenderas att köra den tabelltypen generellt, kan inte alla detaljer men MyISAM är lämpligare för Selects för att den låser hela tabellen vid skrivningar till tabellen.


Citat:

Ursprungligen postat av jonssondesign (Inlägg 20452721)
"Kolla om det finns index på dessa kolumner där "where" används:" - ehm. say what what?
ooh! nej, inget index på varken new eller fresh. as you can se.

Index kan användas även vid Updates eller Deletes,
så om du har t.ex "update tbl where somecolumn = 99" och det inte finns ett index på den kolumnen då görs en scan på alla rader i hela tabellen. med ett index går det snabbare.
Men samtidigt om du lägger till fler index i tabellen kommer inserts gå segare för att den då måste uppdatera index..så detta är kanske ett gränsfall

I alla fall, om det funkar på Binero kanske du inte behöver göra så mycket pill i sql:en för att få det att funka vad gäller prestandabiten

SimonP 2012-10-11 15:17

Citat:

Ursprungligen postat av jonssondesign (Inlägg 20452693)
Men problemet är ju att servern läser filen uppifrån och ned, och utför inget förren allt har bearbetats. Så, om jag skriver echo någonstans i koden, och det finns mer kod nedanför, så uträttas inte det förren hela sidan är färdig, eftersom - vad jag förstår - så bearbetas all php kod i servern, och sen skickar servern en ren html sida till webbläsaren, som sedan visar upp den till mig.
Det vill säga - Allt som finns i koden måste bearbetas, utföras mm, innan servern skickar html koden till webbläsaren. har jag rätt?

Efter echo:t kan du behöva flusha: http://se1.php.net/manual/en/function.flush.php
Funkar ok i Firefox, dock ej i vissa versioner av IE.

Jake.Nu 2012-10-11 17:20

Citat:

Ursprungligen postat av Jonas (Inlägg 20452684)
Har du tex det så här:
PHP-kod:

$xml simplexml_load_file(...); // 50MB stor fil
$rows $data $xml

Så förbrukar du 150MB. Nu är det lite extremt men vill få fram en poäng.

Verkligen inte sant och alldeles för extremt.. Har du testat?

dAEk 2012-10-11 21:11

Grundproblemet är att du verkar har en lösning som nått sin gräns i sin nuvarande miljö.

Antagligen tar minnet slut när du läser in Xml-filen. Vet inte exakt hur det är i PHP men i andra programmeringsspråk kan det handla om filens storlek * 3 eller 4 för att läsa in hela Xml:ns DOM i minnet. Även om det funkar ibland är det inte hållbart i längden. Vem vill använda ett system där det funkar att ladda upp filer ibland? Vad händer om leverantören börjar leverera en lite större fil? Det du säkerligen behöver kika på är att streama filen. När man gör det läser Xml-parsern in filen en bit i taget och det kräver inte i närheten av samma minnesmängd. Tänk att en stream har en fast minnesförbrukning medan att läsa in hela DOM korrelerar med filens storlek, antalet noder samt innehållet i dessa. Man kan hantera filer filer från några få hundra kilobytes till gigabytes med en stream. Det är med andra ord mer skalbart vad gäller minnesförbrukningen. Det är säkert lite bökigare även i PHP men är det givet om man vill få ett väl fungerande system och inte kan styra över filerna som man läser in.

Timeouterna borde du undersöka vad de beror på. Ett tips är att börja logga dina funktionsanrop när det sker fel (läs exceptions). Finns det stacktrace i PHP är de himla användbara att få med i loggarna eftersom de mer eller mindre berättar vad som gått fel. I dokumentationen för anropen borde du se vilka metoder som kan kasta exceptions och vilka typer det kan handla om.

Conny Westh 2012-10-12 13:55

Citat:

Ursprungligen postat av jonssondesign (Inlägg 20452693)
Conny, hur menar du? Kom ihåg - jag är nybörjare.

Vad menar du med "lokal databas"? xampp på datorn? Hur ska det fungera när man kör live? :P Förstår att jag missförstår dig nu.

Helst skulle jag vilja göra såhär:

Köra allt med php, men efter ett visst antal rader som blivit insatta i databasen, så ska det skrivas ut något - laddar kanske?
Därefter så fortsätter den att ladda upp datan igen.

Men problemet är ju att servern läser filen uppifrån och ned, och utför inget förren allt har bearbetats. Så, om jag skriver echo någonstans i koden, och det finns mer kod nedanför, så uträttas inte det förren hela sidan är färdig, eftersom - vad jag förstår - så bearbetas all php kod i servern, och sen skickar servern en ren html sida till webbläsaren, som sedan visar upp den till mig.
Det vill säga - Allt som finns i koden måste bearbetas, utföras mm, innan servern skickar html koden till webbläsaren. har jag rätt?

Jag vill iaf komma bort från att sidan får error pga. långa laddningstider och mycket minne.

Du har en systemlösning som behandlar större mängder data och då bör du först och främst ha en "riktig" relationsdatabas (som MS-SQL, Oracle, Sybase SQL-Anywhere, IBM DB2, Postgre SQL eller MySQL med InnoDB) lokalt på din egen dator.

Du använder XML-filer för överföring av gigantiska datamängder, men du borde först bearbeta ditt data lokalt och sedan bara skicka upp förändringar (inte hela databasen) varje gång du gör en överföring till din webbshop.

När du får datafilen från din leverantör (är det Nelly? och Ellos?) så bör du importera det i din lokala databas först.

Du hade i din PHP-kod en massa villkor för att filtrera bort data vid importen, och det borde du filtrera bort INNAN du skickar upp datatfilen till din webbshop, all data som du filtrerar bort tar ju tid att skicka upp och kostar bandbredd i onödan.

Du bör ha en databastabell för "gender" och en för "category" med en kolumn som innehåller de värden som du vill filtrera bort. Det var det jag visade exempel på i SQL-satsen hur man på ett enkelt sätt kan göra det.

Du bör undvika att använda PHP för databaslogiken, utan lös den logiken med SQL i stället. SQL är hundratals gånger snabbare och effektivare än att först ladda upp alla onödiga data över internet och sen filtrera i ett okompilerat script-språk som PHP.

Conny Westh 2012-10-12 14:22

SQL-sats som borde föregå upload_nelly.php i inlägg 4 ovan.

Du bör mao fört importera hela XML-filen till din lokala datrabas (den tabellen kallar jag för "XML_ImportTabellen
") då du kan köra SQL-frågor för att göra urvalen istället för att loopa i PHP och göra samma sak där.

Du bör ha en tabell med de alternativ för "gender" som du vill EXKLUDERA, den tabellen kallar jag för ExcludeGenderTabellen.

Du bör ha en tabell med de alternativ för "category" som du vill EXKLUDERA, den tabellen kallar jag för ExcludeCategoryTabellen.


Kod:

INSERT INTO products (img, title, brand, store, price, url, category, gender, visible, info, advertiserProductUrl, fresh, new)  ON DUPLICATE KEY UPDATE fresh = 1
SELECT img, title, brand, store, price, url, category, gender, 1, info, advertiserProductUrl, 1, 1
FROM XML_ImportTabellen
WHERE gender NOT IN (select gender from ExcludeGenderTabellen)
AND category NOT IN (select category from ExcludeCategoryTabellen)
AND sale=true

När du sen ska ta fran de data som du ska använda för att skicka upp så hämtar du dem från Products-tabellen med en enkel selectsats:

Kod:

SELECT img, title, brand, store, price, url, category, gender, visible, info, advertiserProductUrl, fresh, new
FROM products

... Vilket blir kortare och tydligare i din kod än den gigantiska PHP-kod du har nu. Det blir betydligt tydligare att läsa och därmed enklare att felsöka.

Clarence 2012-10-12 21:31

Citat:

Ursprungligen postat av dAEk (Inlägg 20452756)
Grundproblemet är att du verkar har en lösning som nått sin gräns i sin nuvarande miljö.

Antagligen tar minnet slut när du läser in Xml-filen. Vet inte exakt hur det är i PHP men i andra programmeringsspråk kan det handla om filens storlek * 3 eller 4 för att läsa in hela Xml:ns DOM i minnet. Även om det funkar ibland är det inte hållbart i längden. Vem vill använda ett system där det funkar att ladda upp filer ibland? Vad händer om leverantören börjar leverera en lite större fil? Det du säkerligen behöver kika på är att streama filen. När man gör det läser Xml-parsern in filen en bit i taget och det kräver inte i närheten av samma minnesmängd. Tänk att en stream har en fast minnesförbrukning medan att läsa in hela DOM korrelerar med filens storlek, antalet noder samt innehållet i dessa. Man kan hantera filer filer från några få hundra kilobytes till gigabytes med en stream. Det är med andra ord mer skalbart vad gäller minnesförbrukningen. Det är säkert lite bökigare även i PHP men är det givet om man vill få ett väl fungerande system och inte kan styra över filerna som man läser in.

Timeouterna borde du undersöka vad de beror på. Ett tips är att börja logga dina funktionsanrop när det sker fel (läs exceptions). Finns det stacktrace i PHP är de himla användbara att få med i loggarna eftersom de mer eller mindre berättar vad som gått fel. I dokumentationen för anropen borde du se vilka metoder som kan kasta exceptions och vilka typer det kan handla om.

Det dAEK skriver är väldigt läsvärt och hjälpande. Ville mest bara fylla på med lite mer specifik info.

Den typen av XML parser som brukar användas för streaming i de flesta språk kallas SAX parser. Den är lite jobbigare att komma igång med just pga dess event-baserade modell. Men som vanligt finns det på php.net exempel och ordentligt med dokumentation: se http://www.php.net/manual/en/book.xml.php

Är du säker på att dina XML-filer inte växer och du har en server som klarar av det är det ingen fara att köra ett enklare API som laddar in hela filen. Men annars bör du verkligen titta på att streama parsingen. Jonas exempel med 50->150mb är inte ens så illa som det kan gå, det beror på datamängd vs attribut och nästling hur mycket minne som behövs.

Sedan bara en amärkning på det om en "riktig relationsdatabas" är ju svårt att ta på. Antagligen så menas en relationsdatabas med tabelltyp som stödjer foreign key constraints, då det kan anses vara "riktiga" relationer. Tipset du fått här är dock att använda en transaktion och rollback om den inte gått igenom. Jag skulle vilja varna lite för att använda det på en delad webhost. För det allra mesta får du på dessa mycket bättre och pålitligare prestanda med MyISAM (båda pga att du med innodb "tävlar" med andra kunder om RAM-cachen men även att de ofta brukar vara så dåligt eller sparsamt konfigurerade för innodb) medans det på dedikerade servers/VPS ofta är precis tvärtom. Ett alternativ är att du skriver till en temporär tabell först, kör samma kontroll och sedan rensar tabellen istället för rollback och ersätter den riktiga tabellen istället för commit. Inte fullt lika säkert, men mer konsekvent resultat.

jonssondesign 2012-10-12 23:55

Tack för alla tips! Jag ska kolla på vad jag klarar av att göra, och återkommer om eventuella frågor. :)

Tack igen!

dAEk 2012-10-16 21:46

Citat:

Ursprungligen postat av Clarence (Inlägg 20452856)
Det dAEK skriver är väldigt läsvärt och hjälpande. Ville mest bara fylla på med lite mer specifik info.
[...]

Tack för de vänliga orden. :)


Alla tider är GMT +2. Klockan är nu 18:03.

Programvara från: vBulletin® Version 3.8.2
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Svensk översättning av: Anders Pettersson