Soruyu tam olarak anlamamış olabilirim
Önce Sarı, Mavi, Yeşil'e basıyorsunuz. Yani beden seçimi yapmıyorsunuz.
Sonra bir daha renk seçimi yapmadan S, M, L'ye basıyorsunuz.
Bu durumda son seçtiğiniz renk (Yeşil) için S, M ve L seçilmiştir diyebiliriz.
Ama Sarı ve Mavi için beden seçimi yapmadığınız için onlarda hangi bedenler seçilmiş bilemeyiz.
Anladığım kadarıyla siz sırası önemli olmaksızın seçilen renk ve beden'lerin her türlü kombinasyonunu elde etmek istiyorsunuz.
Yani sadece Sarı seçilirse, sonra da S ve M seçilirse şu kombinasyon oluşsun:
Sarı/S
Sarı/M
Bundan hemen sonra Mavi seçilirse, seçilmiş her beden bu renk için de geçerli olacak:
Sarı/S
Sarı/M
Mavi/S
Mavi/M
Şimdi de mesela L seçilirse seçilmiş her renk için bu beden de geçerli olacak:
Sarı/S
Sarı/M
Sarı/L
Mavi/S
Mavi/M
Mavi/L
Ben soruyu böyle anladım. Cevabım buna göre olacak.
Örnek olarak üstteki veriyi şu şekilde tutacağım:
$_SESSION["combo"] = [
["Sarı", "S"],
["Sarı", "M"],
["Sarı", "L"],
["Mavi", "S"],
["Mavi", "M"],
["Mavi", "L"],
];
Cevapla alakasız öneri (Okumasanız da olur)
Eğer çok yoğun anlık kullanıcıya sahip bir sistem varsa session kullanımını önermiyorum. Çünkü session sunucunın hafızasında yer tutuyor. (Saklandığı yere göre ram'de veya rom'da olabilir)
Bunun yerine çok anlık kullanıcısı olan sistemlerde token'lar kullanılıyor genellikle. Token'lar şifrelenmiş halde istemci tarafında tutulan, şifresinin sadece sunucudaki anahtarla çözülebildiği string değerlerdir. İstemciye token ile şifrelenmiş halde bazı veriler iletilir ve sunucuya yapılan isteklerde mutlaka token'ın da gönderilmesi istenir. Token yoksa işlem yapılmaz. Token sunucudaki şifreyle çözülür ve değerler kontrol edilerek değiştirilip değişitirilmediği kontrol edilir. Değiştirilmişse yine işlem yapılmaz. Token, her isteğe verilen yanıtta değiştirilerek tekrar istemciye gönderilir. İstemci, her yanıtta gelen token'ı eski token'ın yerine kaydeder ve sonraki istekte göndermek için hazırda tutar. Bu yüzden hem front-end tarafında hem de back-end tarafında token yönetimi için oluşturulmuş fonksiyonlar hazırlanmalıdır.
Session kullanımındaki bir diğer sorun da CSRF açığıdır. Örneğin kullanıcınız yönetim panelinde oturum açtı ve bu kullanıcı için bir session oluşturdunuz. Artık bu kullanıcı tarayıcısını kapatana kadar bu session'la işlemler yapma yetkisine sahip oldu. Sonra kullanıcınıza bir e-posta geldi. E-posta'da da "bana tıkla" diye bir link var. Halen aynı tarayıcıyı kullanan kullanıcı bu linke tıkladı. Meğerse link, sizin sitenizdeki yönetim panelinde kullanıcının bazı verilerinin silinebildiği bir sayfaya GET metoduyla giden bir linkmiş. Sonuçta bu sayfaya atılan istek yine aynı tarayıcıdan geldiği için ve sunucu da bu tarayıcıya özel bir session'la hareket ettiği için gelen isteği karşılar ve silme işlemini yapar. Oysa ki sunucu bir token'ın varlığını kontrol ediyor olsaydı e-posta'yı gönderen kişi bu token'ı önceden bilemeyeceği için bu link'i oluşturamazdı. Zaten token'lar get parametresi olarak değil header üzerinden gönderildiği için bu tip link'lere karşı ayrıca bir güvenlik katmanı daha oluştururlar.
Sunucuda session'ın saklandığı hafıza ile ilgili bir sorununuz yoksa, yani sadece CSRF açığını kapatmak istiyorsanız, session ve token'ı birlikte de kullanabilirsiniz tabii ki.
Asıl Cevap
Siz renk veya bedene her tıklandığında sunucuya bir istek atıyorsunuz anladığım kadarıyla. Verileri de session ile tutuyorsunuz. İstiyorsunuz ki her seçiçen renk/beden değeri session içindeki bir PHP objesinde organize şekilde yerleşsin.
// POST metoduyla PHP'ye ilettiğiniz verileri değişkenlere alalım.
$selectedColor = isset($_POST["color"]) ? $_POST["color"] : false;
$selectedSize = isset($_POST["size"]) ? $_POST["size"] : false;
// Ya renk ya da beden seçilmiştir. İkisi birden aynı anda gelemez diye varsayıyorum.
// Eğer ikisi de gelmemişse bir hata mesajı gönderelim.
if(!$selectedColor && !$selectedSize) {
echo "Renk veya beden seçimi iletilmedi!";
exit();
}
// Daha önceden seçilmiş elemanları tuttuğumuz objeyi de bir değişkene alalım.
$currentSelections = isset($_SESSION["combo"]) ? $_SESSION["combo"] : [];
// Daha önce seçilmiş tüm renkleri ve bedenleri bulalım ve iki ayrı dizide tutalım.
$allCurrentColors = [];
$allCurrentSizes = [];
foreach($currentSelections as $selection) {
if(!in_array($selection[0], $allCurrentColors)) $allCurrentColors[] = $selection[0];
if(!in_array($selection[1], $allCurrentSizes)) $allCurrentSizes[] = $selection[1];
}
// Eğer yeni seçilen değer bir renk ise,
if($selectedColor) {
// Ve bu renk zaten daha önce eklenmişse seçilmiş renklerden kaldırılsın. (Bu renk seçimini iptal etmiş oluyoruz)
if(in_array($selectedColor, $allCurrentColors)) {
$allCurrentColors = array_values(array_diff($allCurrentColors,[$selectedColor]));
}
// Yok eğer bu renk daha önce eklenmemişse seçilmiş renklere ekleyelim.
else {
$allCurrentColors[] = $selectedColor;
}
}
// Eğer yeni seçilen değer bir beden ise, yukarıda renk için uyguladığım kontrolün aynısını beden için yapalım.
if($selectedSize) {
if(in_array($selectedSize, $allCurrentSizes)) {
$allCurrentSizes = array_values(array_diff($allCurrentSizes,[$selectedSize]));
}
else {
$allCurrentSizes[] = $selectedSize;
}
}
// Daha önce renk veya beden için null değeri atamış olabiliriz. (Aşağıda yapıyoruz)
// Şu an için bu iki dizide de null değeri bulunmasın.
$allCurrentColors = array_values(array_diff($allCurrentColors,[null]));
$allCurrentSizes = array_values(array_diff($allCurrentSizes,[null]));
// Böylece elimizde iki ayrı dizide seçilmiş tüm renkler ve tüm bedenler mevcut.
// Bunları birleştireceğiz ve yeni $currentSelections değeri ($_SESSION["combo"] değeri) bu birleşim olacak.
// Öncelikle mevcut seçimleri yok edelim.
$currentSelections = [];
// Şu an sadece renk seçilmiş ama hiç beden seçilmemiş olabilir.
// Veya hiç renk seçilmemiştir ama beden seçilmiştir.
// Ama hiç değeri olmayan türü (renk/beden) yeni oluşturacağımız dizide yok sayamayız.
// O yüzden değeri olmayan tür varsa bir tane null değeri atayalım.
if(count($allCurrentColors)===0) $allCurrentColors = [null];
if(count($allCurrentSizes)===0) $allCurrentSizes = [null];
// Eğer iki dizide birden sadece null değerleri kalmışsa iki diziyi de boşaltalım.
if(count($allCurrentColors)===1 && count($allCurrentSizes)===1) {
if($allCurrentColors[0]===null && $allCurrentSizes[0]===null) {
$allCurrentColors = [];
$allCurrentSizes = [];
}
}
// Her bir renk için her bir beden eklenecek.
// Tüm renkleri dönelim. Her bir renk için...
foreach($allCurrentColors as $currentColor) {
// Tüm bedenleri dönelim. Her bir beden için...
foreach($allCurrentSizes as $currentSize) {
$currentSelections[] = [$currentColor, $currentSize];
}
}
// Tüm seçimler yeniden organize olduğuna göre bu yeni değeri session'a yazabiliriz.
$_SESSION["combo"] = $currentSelections;
Kodları hiç kontrol etmedim, tek seferde çalışmayabilir. Hata ayıklamak gerekebilir...
Yazdığım kodun şu şekilde çalışmasını umuyorum:
Sayfaya ilk girildiğinde herhangi bir renk veya beden seçili değil. Bu durumda PHP'de tutulan değer şu:
$_SESSION["combo"] = [];
Sonra M bedeni seçiliyor. İlk seçim. Bu durumda PHP'de tutulan değer:
$_SESSION["combo"] = [
[null, "M"]
];
Sonra L bedeni seçiliyor. 2. seçim de bu. Bu durumda PHP'de tutulan değer:
$_SESSION["combo"] = [
[null, "M"],
[null, "L"]
];
Sonra da Sarı renk seçilmiş olsun. Bu durumda:
$_SESSION["combo"] = [
["Sarı", "M"],
["Sarı", "L"]
];
Sonra da Mavi renk seçilmiş olsun. Bu durumda:
$_SESSION["combo"] = [
["Sarı", "M"],
["Sarı", "L"],
["Mavi", "M"],
["Mavi", "L"]
];
Sonra da tekrar S beden seçilsin.
$_SESSION["combo"] = [
["Sarı", "M"],
["Sarı", "L"],
["Sarı", "S"],
["Mavi", "M"],
["Mavi", "L"],
["Mavi", "S"]
];
Sonra da Yeşil renk seçilmiş olsun.
$_SESSION["combo"] = [
["Sarı", "M"],
["Sarı", "L"],
["Sarı", "S"],
["Mavi", "M"],
["Mavi", "L"],
["Mavi", "S"],
["Yeşil", "M"],
["Yeşil", "L"],
["Yeşil", "S"]
];
Seçimlerinden hoşnut olmamış olacak ki Mavi renk için tekrar tıklamış olsun. Bu durumda:
$_SESSION["combo"] = [
["Sarı", "M"],
["Sarı", "L"],
["Sarı", "S"],
["Yeşil", "M"],
["Yeşil", "L"],
["Yeşil", "S"]
];
Şimdi de L bedenine tekrar basmış olsun. Seçimleri arasında L olmasını istemiyormuş meğerse:
$_SESSION["combo"] = [
["Sarı", "M"],
["Sarı", "S"],
["Yeşil", "M"],
["Yeşil", "S"]
];
Şimdi bütün renk seçimlerini kaldırmak için önce Sarı sonra Yeşil renk butonlarına basmış olsun. Bu 2 istekten sonra:
$_SESSION["combo"] = [
[null, "M"],
[null, "S"],
];
Şimdi beden seçimlerini de kaldırmak için M ve S butonlarına da bastı diyelim. Sonuçta başa dönmüş olacak:
$_SESSION["combo"] = [];
socket.io kullanmalısın. İnternetteki örneklerde hep öyle kullanılıyor ama socket.io sadece chat uygulamaları yapmak için kullanılmaz.
Örnekleri inceleyip socket.io'nun mantığını kavrayabilirsin. Bu sayede sunucuda gerçekleşen bir durumdan istemcilerin haberdar olmalarını sağlayabilirsin. Uzun bir konu olduğu için burada adım adım anlatmak zor.
Merhaba. Etikete PHP girmişsiniz ama front-end tarafında bu sorunu çözebilirsiniz.
Bir butona tıklandığı anda tarihi alır, üzerine 10dk ekler ve bu değeri butona atribute olarak eklersiniz.
Butona her tıklandığında bu attribute'ü kontrol edersiniz ve henüz zamanı gelmemişse işlem yaptırmazsınız.
Örnek olarak (JQuery ile):
const isButtonFree = ($button) => {
const now = (new Date()).getTime();
const afterTenMinutesTime = (new Date(now + 10 * 60 * 1000)).getTime();
const buttonCode = Number($button.attr('data-code'));
if(isNaN(buttonCode) || buttonCode < now) {
$button.attr('data-code', afterTenMinutesTime);
return true;
}
return false;
};
$(document).on("click", "button.sendToUp", function() {
if(!isButtonFree($(this))) { alert("Son tıklamanın üzerinden 10 dk geçmemiş!"); return; }
// Butona tıklanınca normalde yapılan işlemler...
alert("Butona tıklanabildi");
});
Tarayıcıda konsolu kullanmayı bilenler bu işlemi aşamasınlar, diyorsanız ve butona her tıklama işlemi sunucuya bir istek atıyorsa yukarıda verdiğim kodun üstüne bir de PHP tarafında token oluşturmanız ve her butona basışta sunucu tarafında token kontrolü yapmanız gerekir.
Bu mantıkta da PHP tarafında request'e özel bir token üretecek ve response üzerinden bunu tarayıcıya göndereceksiniz.
Tarayıcı bu kodu alıp butona attribute olarak basacak.
Butona her basışta attribute üzerinden bu token değeri okunacak ve request'e eklenecek.
Bu token, içinde 10 dk sonrasının verisini şifreli şekilde tutan ve şifrenin anahtarını bilmeyenin şifreyi çözemeyeceği bir kod olmalı.
Bu konu, Tayfun ERBİLEN'in PHP ile Verileri Şifrelemek makalesinden yardım alınarak çözülebilir.
Şimdi diyelim ki siz butona basıldığında işlem yapıp yapmayacağınızı belirlemek için MoveUp.php sayfasına post isteği atıyorsunuz ve yanıta göre işlem yapıp yapmayacağınıza karar veriyorsunuz.
MoveUp.php
// Aynı kodları tekrar tekrar yazmamak için bir fonksiyon...
function sendResponse($val) {
header("Content-type: application/json; charset=utf-8");
echo json_encode($val);
exit();
}
// Token şifreleme algoritması ve şifre anahtarı verileri...
$tokenKey = 'prototurk.2022.xx1';
$tokenCipher = 'AES-128-ECB';
// POST parametrelerini alalım.
$token = isset($_POST["token"]) ? $_POST["token"] : null;
$id = isset($_POST["id"]) ? $_POST["id"] : null; // başka parametreler de göndermişsiniz diyelim...
// token veya id değeri yoksa hata döndürelim.
if(!$token) sendResponse(["error"=>"token required", "errorCode"=>1]);
if(!$id) sendResponse(["error"=>"id required", "errorCode"=>1]);
// token çözülememişse (değiştirilmiştir muhtemelen) hata dönelim.
$decodedToken = openssl_decrypt($token, $tokenCipher, $tokenKey);
if(!$decodedToken) sendResponse(["error"=>"token hatalı", "errorCode"=>2]);
// token içindeki değer bir timestamp değeri. Bu değeri şu anın timestamp'iyle karşılaştıralım.
// Eğer henüz token'daki tarih şu anki tarihten önce değilse henüz 10 dakikanın geçmediğine dair hata dönelim.
$tokenTime = (int)$decodedToken;
$now = strtotime("now");
if($now < $tokenTime) sendResponse(["error"=>"It hasn't been 10 minutes.", "errorCode"=>3);
// Buraya kadar her şey yolundaysa butonun 10 dakikası geçmiş demektir.
// Şu andan itibaren 10 dakika sonrasını bulup şifreleyelim.
$afterTenMinutes = strtotime("+10 minute");
$newToken = openssl_encrypt($afterTenMinutes, $tokenCipher, $tokenKey);
// Bundan sonra id gibi diğer parametreleri kullanarak gerekli işlemlerimizi yapıyoruz.
// Sonuç olarak döneceğimiz cevapta mutlaka yeni token değerini de gönderiyoruz ki butonun attribute'sine yeni token değeri yazılabilsin.
sendResponse["success"=>"It can move to up!", "token"=>$newToken];
Bu sayfa sizin butona basıldıktan sonra sunucuya istek attığınız ve sunucu taraflı işleri hallettiğiniz sayfa.
Bu sayfaya POST metoduyla "token" değerini göndermezseniz veya değiştirilmiş bir token gönderirseniz size hata verir.
Haliyle butonlar ilk oluştukları zamanda herhangi bir token değerine sahip olmadıkları için bu sayfaya atacağınız her istek hata döner.
Bu nedenle butonları oluştururken hemen token'larını da vermelisiniz.
Örneğin butonlarınızın bulunduğu front-end sayfanız şöyle olacak:
<?php
// Token şifreleme algoritması ve şifre anahtarı verileri...
$tokenKey = 'prototurk.2022.xx1';
$tokenCipher = 'AES-128-ECB';
$firstToken = openssl_encrypt(strtotime("+10 minute"), $tokenCipher, $tokenKey);
?>
<div class="moveableElement" data-id="123">
<div class="elementTitle">Örnek içerik 1</div>
<button class="moveToUpButton" data-token="<?=$firstToken?>">Yukarı Taşı</button>
</div>
<div class="moveableElement" data-id="132">
<div class="elementTitle">Örnek içerik 2</div>
<button class="moveToUpButton" data-token="<?=$firstToken?>">Yukarı Taşı</button>
</div>
<div class="moveableElement" data-id="231">
<div class="elementTitle">Örnek içerik 3</div>
<button class="moveToUpButton" data-token="<?=$firstToken?>">Yukarı Taşı</button>
</div>
Bu örnekte sayfaya girildiği andan itibaren tüm butonlar, 10 dakika sonrasının şifrelenmiş halini token olarak almış olacaklar.
Yani biri sayfaya ilk kez girerse veya eskaza sayfayı yenilerse 10 dk boyunca yukarı taşıma yapamayacak.
Şimdi daha önce front-end taraflı kontrol sağladığımız kodda istek atma örneği yapalım.
Diyelim ki butona basınca üst elementin data-id
attribute'sini alıp veriyi PHP'ye ajax ile iletiyoruz...
const isButtonFree = ($button) => {
const now = (new Date()).getTime();
const afterTenMinutesTime = (new Date(now + 10 * 60 * 1000)).getTime();
const buttonCode = Number($button.attr('data-code'));
if(isNaN(buttonCode) || buttonCode < now) {
$button.attr('data-code', afterTenMinutesTime);
return true;
}
return false;
};
$(document).on("click", "button.sendToUp", function() {
const $thisButton = $(this);
if(!isButtonFree($thisButton)) { alert("Son tıklamanın üzerinden 10 dk geçmemiş!"); return; }
const itemId = $(this).closest(".moveableElement").attr("data-id");
const itemToken = $(this).attr("data-token");
$.ajax({
url:"MoveUp.php",
type:"POST",
data: { id:itemId, token:itemToken },
success: (res) => {
// Eğer sunucu token göndermişse butondaki data-token'ı hemen güncelleyelim.
if(res.token) $thisButton.attr('data-token', res.token);
if(res.error) {
if(res.errorCode===1) alert("eksik parametre");
else if(res.errorCode===2) alert("token hatalı! Sayfayı yenileyin");
else if(res.errorCode===3) alert("Henüz 10 dakika geçmemiş...");
return;
}
if(res.success) {
alert("Tamam. Yukarı Taşıma işlemi yapılabilir...");
return;
}
},
error: (err) => {
alert("Sunucuyla iletişimde problem...");
}
});
});
Muhakkak sizin yapmak istediğiniz şeyler biraz daha farklıdır. Ama bu cevaptan yola çıkarak fikir yürütebilirsiniz. Bundan daha detaylı cevap alabileceğinizi sanmıyorum. :)
Aslında ilk yazdığım kod bloğundaki işlemler çoğu kullanıcının sürekli Yukarı Taşı yapmasını önleyecektir.
PHP taraflı kontrollerimizle kötü niyetli kullanıcıların bir çoğunu engellemiş olduk.
Peki kötü niyetli bir kullanıcı bu token'la kurduğumuz güvenlik katmanını aşamaz mı? Yeterince tecrübeliyse kolaylıkla aşabilir. Mesela sayfa ilk yüklendiğinde butona verilen token'ı kopyalar. Sonra ilk Yukarı Taşıma işleminden sonra biz javascript'le yeni token'ı butona veririz. Evet bu token'ı silerse veya değiştirirse işlem yapamaz ama ilk kopyaladığı token halen PHP tarafından kabul edilen bir token olduğuna göre bu token'ı istediği butona yapıştırıp tekrar tekrar Yukarı Taşıma işlemi yapabilir. Peki bu kötü niyetli kullanıcı için ne yapabiliriz? Mesela şifreleme yaparken token'a sadece tarih verisini değil aynı zamanda data-id attribute'sindeki değeri de katarız. Böylece her butonun kendine özgü bir token'ı olur. MoveUp.php sayfası da hem tarihi hem de id değerini birlikte konrol eder. Peki böyle yaparak her butonun kendine özgü bir token'ı olmasını sağlamak bu kötü niyetli kullanıcıyı durdurabilir mi? Hayır. Sadece aynı kodu istediği her butonda kullanamamasını sağlamış oluruz. Ama her butonun token'ını yine de kopyala yapıştırla yeniden kullanabilir. Demek ki bir token'ın kullanıldıktan sonra tekrar kullanılamamasını sağlayacak bir algoritmamız olması lazım...
Bunlar daha derinlikli siber güvenlik konuları olduğundan bambaşka bir soru'nun konusu olabilir...
Bir kez daha bakınca aslında daha kısa SQL oluşmasını sağlayabilirmişiz gibi göründü.
Mesela ilk foreach
döngüsünde oluşturup $whereCondition
değişkenine atadığımız string örnek olarak şöyle:
(orderId='123' auth='abc') OR (orderId='131' auth='abc') OR (orderId='213' auth='abc')
yani aslında auth sütunu hep aynı. O halde bunu tek bir AND ifadesiyle birleştirebilirdik. Yani:
auth='abc' AND (orderId='123' OR orderId='131' OR orderId='213')
şeklinde yazabilirdik. Muhtemelen az da olsa MySQL için de daha rahat bir sorgu olacaktır. İki sütunu her OR
bloğu için kontrol etmemiş olur.
Bu durumda aşağıdaki şu satırları:
foreach ($detail as $keys) {
$whereConditions[] = "(orderId='" . $keys["orderId"] . "' AND auth='" . $this->appkey . "')";
$whereConditionsWithESD[] = "(ESD.orderId='" . $keys["orderId"] . "' AND ESD.auth='" . $this->appkey . "')";
}
$whereCondition = implode(" OR ", $whereConditions);
$whereConditionWithESD = implode(" OR ", $whereConditionsWithESD);
şu şekilde değiştirmek daha yerinde olur:
foreach ($detail as $keys) {
$orderId = $keys["orderId"];
$whereConditions[] = "orderId='$orderId'";
$whereConditionsWithESD[] = "ESD.orderId='$orderId'";
}
$whereCondition = implode(" OR ", $whereConditions);
$whereConditionWithESD = implode(" OR ", $whereConditionsWithESD);
$appKey = $this->appkey;
$whereCondition = "auth='$appKey' AND ($whereCondition)";
$whereConditionWithESD = "ESD.auth='$appKey' AND ($whereCondition)";
Böylece satır sayısı biraz arttı ama MySQL için daha rahat bir sorgu oluşturduk.
Aşağıdaki satırlarda sanırım $orderList
objesindeki değer kodunuzdaki diğer kurgularla uyuşmadığı için yeni bir formata çevirerek orderId değerlerine göre gruplamışsınız.
O kısmı şu şekilde değiştirirseniz daha kısa kodla işi çözebilirsiniz sanıyorum.
Yani yukarıdaki kodlardan sonra gelen kodlarınızdaki $orderListESD = [];
ile başlayıp $orderList = $orderListESD;
ile biten kısmı komple silip yerine şunu yazabilirsiniz:
$relationalOrderList = [];
foreach($orderList["detail"] as $detail) {
$orderId = $detail["orderId"];
$relationalOrderList[] = [
"detail" => $detail,
"address" => array_filter($orderList["address"], function($item){ return $item["orderId"]===$orderId; }),
"cargoDetail" => array_filter($orderList["cargoDetail"], function($item){ return $item["orderId"]===$orderId; }),
"customerDetail" => array_filter($orderList["customerDetail"], function($item){ return $item["orderId"]===$orderId; }),
"list" => array_filter($orderList["list"], function($item){ return $item["orderId"]===$orderId; }),
];
}
$orderList = $relationalOrderList;
<div>
<input type="hidden" name="content_id" value="123">
<input type="text" name="content_detail" class="form-control" value="abc1">
</div>
<div>
<input type="hidden" name="content_id" value="132">
<input type="text" name="content_detail" class="form-control" value="abc2">
</div>
<div>
<input type="hidden" name="content_id" value="213">
<input type="text" name="content_detail" class="form-control" value="abc3">
</div>
<div>
<input type="hidden" name="content_id" value="231">
<input type="text" name="content_detail" class="form-control" value="abc4">
</div>
Bu şekilde input'lar var ve data
key'iyle post ediliyorlar.
content_id ve content_detail dizi olarak PHP'ye iletiliyor ve bu dizilerin eleman sayıları aynı.
HTML'de de doğru sıralamayla dizilmişlerse yukarıdaki html koduna benzer yapıdan giden POST isteğinin PHP'de şu şekilde bir karşılığı oluşuyor olmalı:
$_POST["data"]["content_id"] = ["123", "132", "213", "231"];
$_POST["data"]["content_detail"] = ["abc1", "abc2", "abc3", "abc4"];
Bunları tek bir çatı altında birleştirmek istiyorsanız:
$content = [];
$contentCount = count($_POST["data"]["content_id"]);
for($i=0; $i < $contentCount; $i++) {
$content[] = [
"id" => $_POST["data"]["content_id"][$i],
"detail" => $_POST["data"]["content_detail"][$i]
];
}
echo "<pre>";
print_r($content);
echo "</pre>";
Böylece $content
değişkeninde verileri toplamış oldunuz.
Video'yu izlemedim ama cevap gelmediği için şöyle bir yorum yapayım...
Promise kullanmasaydı ve request()
fonksiyonunu async
key'i ile tanımlasaydı da aynı işlemi yapabilirdi.
Promise kullanarak hangi durumda then()
veya hangi durumda catch()
bloğuna düşeceğini resolve()
ve reject()
fonksiyonlarıyla belirleyebildi.
request(url,data)
.then(result=>{
/* resolve() ile gönderilen result değeri buraya geldi */
})
.catch(result=>{
/* reject() ile gönderilen result değeri buraya geldi */
});
Peki aynı fonksiyonu Promise
olmadan yazsaydı?
function async request(url, data = false, method = 'GET') {
const options = {
method
}
if (data && method === 'POST') {
options.body = JSON.stringify(data)
}
const response = await fetch(url, options)
const result = await response.json();
if (response.ok && response.status === 200) {
return result;
} else {
throw result;
}
}
Böyle yazsaydık request()
fonksiyonunu şöyle kullanacaktık:
request(url,data)
.then(result=>{
/* return ile gönderilen result değeri buraya geldi */
})
.catch(result=>{
/* throw ile gönderilen result değeri buraya geldi */
});
Evet, Promise kullanmasa da olurmuş. :))
Belki fonksiyonu geliştirmeyi, anlamsal olarak ayrıştırmayı veya paralel istekler atabilme ihtimalini düşünmüştür ama sonra gerek kalmamıştır.
Mesela request()
fonksiyonunun paralel çalışacak 2 işlem yürütmesi gerekiyor ama bunların sadece birinin sonucunu alacak diyelim.
function request(url, data = false, method = 'GET') {
new Promise(async(resolve, reject) => {
setTimeout(()=>{ console.log("Ben de çalıştım!"); }, 4000);
});
return new Promise(async (resolve, reject) => {
const options = {
method
}
if (data && method === 'POST') {
options.body = JSON.stringify(data)
}
const response = await fetch(url, options)
const result = await response.json();
if (response.ok && response.status === 200) {
resolve(result)
} else {
reject(result)
}
})
}
Böylece request()
fonksiyonu çalıştığı anda ilk promise devreye girdi. Beklemeden 2.promise'ı return etti.
Tek fonksiyonda paralel iki işlem yürütmüş oldu. İlk promise 4 saniye sonra işlemini tamamlayacak. Ama request()
fonksiyonu mutlaka 2. promise'ın tamamlandığı zaman sonuç döndürecek.
Belki de sadece anlamsal olarak Promise kullanmak istemiş olabilir.
Yani request()
fonksiyonuna baktığı zaman burada asenkron bir işlem olduğunu anlamak Promise
adını görünce onun için daha hızlı şekilde anlaşılır oluyordur.
<div class="card">
<div class="imageSide">
<img src="https://picsum.photos/100" />
</div>
<div class="informationSide">
<div class="information">
<strong>120.235</strong>
<span>TAKİPÇİLER</span>
</div>
<div class="information">
<strong>120.235</strong>
<span>TAKİP EDİLEN</span>
</div>
</div>
</div>
.card, .card * {
display:flex;
margin:0;
padding:0;
flex-direction:column;
}
.card {
width:350px;
border:1px solid gray;
border-radius:10px;
background:#09c;
flex-direction:row;
align-items:center;
justify-content:space-between;
}
.card > .imageSide {
}
.card > .imageSide > img {
border-radius:50%;
width:100px;
margin:15px;
}
.card > .informationSide {
flex-direction:row;
flex-wrap: wrap;
align-items:center;
justify-content:flex-end;
margin-left:auto;
}
.card > .informationSide > .information {
width:100px;
text-align:center;
padding:5px 15px;
}
.card > .informationSide > .information > strong {
font-size:22px;
}
.card > .informationSide > .information > span {
font-size:12px;
}
.card
elementinin width
değerini artırdığınızda, takip elementlerinin yan yana geldiğini görebilirsiniz.
Bunu sağlayan flex tasarımda kullanılan flex-wrap
özelliğidir.
foreach içinde sql sorgusu atmamalısınız.
Hepsinde ortak olan sanırım orderId ve auth sütunları.
İlk sorgunuzdan sonra bu orderId ve auth değerlerini bir string içinde birleştirin ve or'larla birbirine bağlayın.
$detail = $this->connect->query("...");
$rowPD = $detail->rowCount();
$orderList = [];
$whereConditions = [];
$whereConditionsWithESD = [];
if ($rowPD > 0) {
$detail = $detail->fetchAll(PDO::FETCH_ASSOC);
foreach ($detail as $keys) {
$whereConditions[] = "(orderId='".$keys["orderId"]."' AND auth='".$this->appkey."')";
$whereConditionsWithESD[] = "(ESD.orderId='".$keys["orderId"]."' AND ESD.auth='".$this->appkey."')";
}
$whereCondition = implode(" OR ", $whereConditions);
$whereConditionWithESD = implode(" OR ", $whereConditionsWithESD);
$detail = $this->connect->query("... WHERE $whereCondition")->fetch(PDO::FETCH_ASSOC);
$adress = $this->connect->query("... WHERE $whereCondition")->fetchAll(PDO::FETCH_ASSOC);
$cargoDetail = $this->connect->query("... WHERE $whereCondition")->fetch(PDO::FETCH_ASSOC);
$customerDetail = $this->connect->query("... WHERE $whereCondition")->fetch(PDO::FETCH_ASSOC);
$list = $this->connect->query("... WHERE $whereConditionWithESD ...")->fetchAll(PDO::FETCH_ASSOC);
$orderList = [
"detail" => $detail,
"adress" => $adress,
"cargoDetail" => $cargoDetail,
"customerDetail" => $customerDetail,
"list" => $list
];
}
Sorgularınızın içeriğini bilmiyorum ama hepsinin WHERE koşulu aynı. sadece WHERE koşullarını yukarıdaki gibi düzenleyebilirsiniz.
Örneğin foreach içindeki ilk 4 sorgunuzdaki
where orderId='$orderId' and auth='$this->appkey'
yerine
WHERE $whereCondition
yazacaksınız.
$list
için olan son sorguda da
WHERE ESD.orderId = '$orderId' AND ESD.auth = '$this->appkey'
yerine
WHERE $whereConditionWithESD
yazacaksınız.
Böylece mesela $detail
için her foreach döngüsünde
WHERE orderId='123' auth='abc'
,
WHERE orderId='131' auth='abc'
,
WHERE orderId='213' auth='abc'
şeklinde yazıp istek atmadık da,
tek bir $detail
sorgusu için
WHERE (orderId='123' auth='abc') OR (orderId='131' auth='abc') OR (orderId='213' auth='abc')
şeklinde istek attık.
Yani mesela ilk sorgunuzdan 3 tane orderId döndüyse
foreach içindeki 5 sorgunuz 3 defa çalışacak ve MySQL'e 15 defa istek atacaksınız.
Yeni durumdaysa kaç tane orderId olursa olsun MySQL'e sadece 6 istek atacaksınız.
Veritabanına mümkün olduğunca az istek atılmalı. Bu işlem tek başına daha hızlı sonuç almanızı sağlayacaktır.
Eğer $list
sorgunuzda olduğu gibi diğer sorguları da tek sorguda birleştirme şansınız varsa çok daha iyi sonuç alırsınız.
MySQL ile mümkün olduğunca az bağlantı kurmalısınız.
NOT: bu işlem sonunca var_dump()
ile $orderList
değişkeninizi kontrol etmeniz gerekiyor. Çünkü sizin aldığınız json formatıyla bu yeni json formatı artık farklı. Ama her iki durumda da veritabanındaki aynı değerler artık elinizde.
Merhaba @262efe.
Input'tan aldığın değerler varsayılan olarak string türündedir. Input'tan gelen değerin sayı olup olmadığını isNaN()
fonksiyonuyla test edebilirsin. Sonra da Number()
fonksiyonuyla bu değeri sayıya çevirmelisin. Daha sonra işleme sokabilirsin. Eğer bölme işlemi yapacaksan bölen sayının sıfır olmadığına emin olmalısın çünkü sıfıra bölümler de NaN döner.
Input'tan aldığın verinin bir sayıyla çarpımıyla ilgili örnek:
<input id=userNumber type=number placeholder="Bir sayı girin" />
<button id=calculationButton>Hesapla</button>
<div id=resultArea></div>
// Butonu id attribute'si ile seçiyorum ve $calculationButton adlı değişkene atıyorum.
// Zorunlu değil ama bir elementi değişkene atıyorsak değişken adını $ işaretiyle başlatırız.
const $calculationButton = document.getElementById("calculationButton");
// input elementini de bir değişkene alıyorum.
const $userNumber = document.getElementById("userNumber");
// Sonucu yazacağımız div elementini de bir değişkene alıyorum.
const $resultArea = document.getElementById("resultArea");
// input'a girilen sayıyı hangi sayıyla çarpacağımızı da belirleyelim.
const constantNumber = 2;
// Butonun click olayını dinliyorum.
$calculationButton.addEventListener("click", function() {
// input'taki değeri bir değişkene alıyorum.
// Bu değişkenin değerini daha sonra değiştireceğim için const yerine let kullanarak oluşturuyorum.
let inputValue = $userNumber.value;
// Aldığım değerin sayı olup olmadığını kontrol ediyorum.
// Eğer sayı değilse bir uyarı verip işlem yapılmasını engelliyorum.
if(isNaN(inputValue)) {
$resultArea.innerHTML = "Bir sayı değeri girmelisiniz!"; // div'e uyarı mesajını yazdım.
$userNumber.value = ""; // Input'a yazılan değeri sildim.
return; // Bu fonksiyonun bu adımdan sonra çalışmamasını sağladım.
}
// Input sayısal değer içeren bir string'tir.
// Örneğin "12" bir string'tir ama tamamen sayılardan oluştuğu için
// isNaN() fonksiyonu bunun bir sayı olduğunu söyler. Biz bunu "12" değil 12 yapacağız:
inputValue = Number(inputValue);
// Artık güvenle çarpma işlemimizi yapabiliriz.
// Bölme işlemi yapmayacağımız için sayının 0 olup olmaması önemli değil.
// Sabit sayımız olan constantNumber ile input'un değeri olan inputValue'yi çarpabiliriz.
const result = constantNumber * inputValue;
// Bulduğum değeri resultArea id'li div elementine yazıyorum:
$resultArea.innerHTML = constantNumber + " x " + inputValue + " = " + result;
});
Mobil uygulama geliştirici olmak istiyorsanız, sırayla:
- İngilizce
- Javascript'i ilerletme, ES6, ES7, ES8, ES9, ES10...
- React Native
- TypeScript (ben sevmem)
Web geliştirici olmak istiyorsanız, sırayla:
- Html, css, javascript
- İngilizce
- Javascript'i ilerletme, ES6, ES7, ES8, ES9, ES10, ES11...
- React
- TypeScript
- Next.js
Mobil veya Web aşamalarından sonra sonra
- Express.js ile web servis geliştirme
- MySql veya MongoDb
- Redis
- Docker, Nginx
...
Neden React üzerinden yürüyoruz? Çünkü piyasaya hakim.
Svelte ile ilgili 1 iş, Vue ile ilgili 15 iş, Angular'la 20 iş, React ile ilgili 50 iş bulursunuz.
Mobil tarafta da Flutter ile 10 iş buluyorsanız React Native ile 50 iş bulursunuz.
Oranları uydurdum ama olay tamamen piyasa hakimiyeti. Bu hakimiyet hem Türkiye'de hem yurt dışında böyle. Uzun zaman da bu hakimiyet bozulmayacaktır. Bozulacak olsa Svelte gelir ve hepsini piyasadan silerdi. Deno gelir ve Javascript'i bitirirdi. Node.js gelir Apache'yi bitirirdi ve PHP projesi sonlanırdı. Bitirebildiler mi? Bitiremediler. Kolay kolay da bitiremezler.
Geçmişte piyasaya hakimiyet kurup da bir anda sonlanmış proje hiç yok mu? Var. Ama bu çok çok nadir yaşanan bir durum. Nitekim öldü bitti diye 10 yıldır yaygara koparılan PHP halen çok yaygın şekilde kullanılıyor.