Constructing large PHP query - php

I'm working with WordPress with custom fields and trying to convert a query which was initially written for pjjtextbase (textfile database) to search a csv file for matching properties for rent/sale.
I understand that to query a custom posts in WP with custom fields I would write something like;
$location = preg_replace('/^-|-$|[^-a-zA-Z0-9]/', '', $_GET['location']);
$type = preg_replace('/^-|-$|[^-a-zA-Z0-9]/', '', $_GET['type']);
$buyrent = preg_replace('/^-|-$|[^-a-zA-Z0-9]/', '', $_GET['buyrent']);
$min_val =preg_replace('/[^0-9]/', '', $_GET['min_val']);
$room_no_min = preg_replace('/[^0-9]/', '', $_GET['room_no_min']);
// This replaces characters in the input string if need be for security reasons
$args =
array(
'post_type' => 'properties',
'meta_query' =>
array(
'key' => 'wp_location',
'value' => $location,
'compare' => '=',
)
);
$results = new WP_Query($args);
But when I look at the code the programmer wrote
/***** THESE VALUES NEEDED FOR SELECT BOX *****/
$locations = ptb_listUnique($info, 'Property_Location');
$property_type = ptb_listUnique($info, 'Property_Type');
$property_buy_rent = ptb_listUnique($info, 'Buy_Rent');
//check property
$query = Array();
if(!empty($_GET['location'])) {
$query[] = "'Property_Location' == '".$_GET['location']."'";
}
if(!empty($_GET['type'])) {
$query[] = "'Property_Type' == '".$_GET['type']."'";
}
if(!empty($_GET['buyrent'])) {
$query[] = "'Buy_Rent' == '".$_GET['buyrent']."'";
}
//check price
if(empty($_GET['buyrent']) || $_GET['buyrent']=="Buy"){
if(!empty($_GET["min_val_buy"]) && is_numeric($_GET["min_val_buy"])){
$min_val_buy = $_GET["min_val_buy"];
}
if(!empty($_GET["max_val_buy"]) && is_numeric($_GET["max_val_buy"])){
$max_val_buy = $_GET["max_val_buy"];
}
$min_val_buy = min($_GET["min_val_buy"], $_GET["max_val_buy"]);
$max_val_buy = max($_GET["min_val_buy"], $_GET["max_val_buy"]);
$query[] = "'Price_Rent' >= '".$min_val_buy."'";
$query[] = "'Price_Rent' <= '".$max_val_buy."'";
}else{
if(!empty($_GET["min_val_rent"]) && is_numeric($_GET["min_val_rent"])){
$min_val = $_GET["min_val_rent"];
}
if(!empty($_GET["max_val_rent"]) && is_numeric($_GET["max_val_rent"])){
$max_val = $_GET["max_val_rent"];
}
$min_val_rent = min($_GET["min_val_rent"], $_GET["max_val_rent"]);
$max_val_rent = max($_GET["min_val_rent"], $_GET["max_val_rent"]);
$query[] = "'Price_Rent' >= '".$min_val_rent."'";
$query[] = "'Price_Rent' <= '".$max_val_rent."'";
}
//check bedrooms
if(!empty($_GET["room_no_min"]) && is_numeric($_GET["room_no_min"])){
$room_min_val = $_GET["room_no_min"];
}
if(!empty($_GET["room_no_max"]) && is_numeric($_GET["room_no_max"])){
$room_max_val = $_GET["room_no_max"];
}
$room_min_val = min($_GET["room_no_min"], $_GET["room_no_max"]);
$room_max_val = max($_GET["room_no_min"], $_GET["room_no_max"]);
$query[] = "'No_Bedrooms' >= '".$room_min_val."'";
$query[] = "'No_Bedrooms' <= '".$room_max_val."'";
//Now build the query to search
$query = implode(' AND ', $query);
It dawned on me that he is not creating multiple queries and then using IMPLODE to combine them but rather creating one whole string then using IMPLODE to add the AND to create the actual query.
The custom fields I have in WP are exactly the same as the large code variable except they are preceded with wp_.
So my question is how do I go about converting his code into a WordPress query?

Related

What's the best way to compare / insert / update products in a MySQL db from a .CSV file

At our company we pull a .CSV file from the suppliers FTP server and update our product data (price, stock,..) each morning.
We wrote a cron for this task as it should run automatically.
The current script is working in most cases. However, sometimes we recieve an error: 'Allowed memory size of 134217728 bytes exhausted (tried to allocate 75 bytes)'.
We use CodeIgniter with DataMapper ORM. A possible design error might be the fact that the script is working with objects instead of array's...
Each time 49000 rows are checked.
Can anyone help us find another way of doing this?
The following script is the function that runs after the files are copied.
// Include auth connection params
$udb = $this->_completeParams($db);
// Check if an update was downloaded
$supplier = new Supplier(NULL,$udb);
$supplier->where(array('alias'=>'XX','name'=>'xxxxxxxxx'))->get(1);
$cronStart = date('Y-m-d H:i:s');
$cronStartDate = date('Y-m-d');
//mail($this->adminMail, 'CRON', 'Gestart:' .$cronStart, $this->headerMail);
//$message .= '1: '.memory_get_usage()."\r\n";
if($supplier->import_found) {
//if(true) {
$rows = 0;
$updated = 0;
$new = 0;
//$aAvailable = array();
$message .= '<h3>Start: '.$cronStart.'</h3>' . "\r\n";
$object = new Supplier_product(NULL,$udb);
$cat = new Supplier_category(NULL, $udb);
$manu = new Supplier_manufacturer(NULL, $udb);
$auvibel = new Supplier_auvibel(NULL, $udb);
$bebat = new Supplier_bebat(NULL, $udb);
$recupel = new Supplier_recupel(NULL, $udb);
$reprobel = new Supplier_reprobel(NULL, $udb);
$files = glob($this->tempDir.'XXXXX/prices/*');
foreach($files as $file) {
$ext = pathinfo($file, PATHINFO_EXTENSION);
$data = ($ext == 'txt')?$this->_csvToArray($file, ';'):false;
// If the CSV data is in $data
if($data !== false) {
$totalCount = count($data);
for($i = 0; $i <= $totalCount; $i++) {
//$aAvailable[] = $data[$i]['ArtID'];
$rows++;
//$message .= 'loop start: '.memory_get_usage()."\r\n";
$object->where(array('art_id'=>$data[$i]['ArtID'],'supplier_id'=>$supplier->id))->get(1);
$auvibel->select('value')->where(array('art_id'=>$data[$i]['ArtID'], 'supplier_id'=>$supplier->id))->get(1);
$auvibel->value = ($auvibel->exists())?$auvibel->value:0;
$bebat->select('value')->where(array('art_id'=>$data[$i]['ArtID'], 'supplier_id'=>$supplier->id))->get(1);
$bebat->value = ($bebat->exists())?$bebat->value:0;
$recupel->select('value')->where(array('art_id'=>$data[$i]['ArtID'], 'supplier_id'=>$supplier->id))->get(1);
$recupel->value = ($recupel->exists())?$recupel->value:0;
$reprobel->select('value')->where(array('art_id'=>$data[$i]['ArtID'], 'supplier_id'=>$supplier->id))->get(1);
$reprobel->value = ($reprobel->exists())?$reprobel->value:0;
$intrastat = 0;
$data[$i]['LP_Eur'] = ($data[$i]['LP_Eur'] != '')?str_replace(',', '.', $data[$i]['LP_Eur']):0;
$data[$i]['DE_Eur'] = ($data[$i]['DE_Eur'] != '')?str_replace(',', '.', $data[$i]['DE_Eur']):0;
$data[$i]['D1_Eur'] = ($data[$i]['D1_Eur'] != '')?str_replace(',', '.', $data[$i]['D1_Eur']):0;
$data[$i]['D1_Eur'] = ($data[$i]['D2_Eur'] != '')?str_replace(',', '.', $data[$i]['D2_Eur']):0;
$data[$i]['PricePersonal_Eur'] = ($data[$i]['PricePersonal_Eur'] != '')?str_replace(',', '.', $data[$i]['PricePersonal_Eur']):0;
$data[$i]['BackorderDate'] = ($data[$i]['BackorderDate'] != '')?date('Y-m-d', strtotime($data[$i]['BackorderDate'])):NULL;
$data[$i]['ModifDate'] = ($data[$i]['ModifDate'] != '')?date('Y-m-d', strtotime($data[$i]['ModifDate'])):NULL;
if($object->exists()) {
if($object->allow_cron_update) { //if($data[$i]['ModifDate'] != $object->modified) {
// Check if category group exists
$cat->select('id')->where(array(
'supplier_id' => $supplier->id,
'name_a' => $data[$i]['Class1'],
'name_b' => $data[$i]['Class2'],
'name_c' => $data[$i]['Class3'],
))->get(1);
if(!$cat->exists()) {
// Category should be added
$cat->supplier_id = $supplier->id;
$cat->name_a = $data[$i]['Class1'];
$cat->name_b = $data[$i]['Class2'];
$cat->name_c = $data[$i]['Class3'];
$cat->save();
// Log as notification: New supplier categorie
$this->_notify('Niewe categorie',array(
'body' => $supplier->name.' heeft "'.$cat->name_a.' - '.$cat->name_b.' - '.$cat->name_c.'" als nieuwe categorie toegevoegd.',
'controller' => 'leveranciers',
'trigger' => 'new_supplier_category',
'url' => base_url().'leveranciers/item/'.$supplier->id.'/categorien',
'icon' => 'icon-truck',
'udb' => $udb,
));
}
// Check if manufacturer exists
$manu->select('id')->where(array(
'name' => $data[$i]['PublisherName']
))->get(1);
if(!$manu->exists()) {
// Manufacturer should be added
$manu->name = $data[$i]['PublisherName'];
$manu->save($supplier);
}
// Add the product to the database
$object->art_id = $data[$i]['ArtID'];
$object->supplier_id = $supplier->id;
$object->supplier_category_id = $cat->id;
$object->supplier_manufacturer_id = $manu->id;
$object->part_id = $data[$i]['PartID'];
$object->ean_code = $data[$i]['EanCode'];
$object->name = $data[$i]['Description'];
$object->description = NULL;
$object->version = $data[$i]['Version'];
$object->language = $data[$i]['Language'];
$object->media = $data[$i]['Media'];
$object->trend = $data[$i]['Trend'];
$object->price_group = $data[$i]['PriceGroup'];
$object->price_code = $data[$i]['PriceCode'];
$object->eur_lp = $data[$i]['LP_Eur'];
$object->eur_de = $data[$i]['DE_Eur'];
$object->eur_d1 = $data[$i]['D1_Eur'];
$object->eur_d2 = $data[$i]['D2_Eur'];
$object->eur_personal = $data[$i]['PricePersonal_Eur'];
$object->stock = $data[$i]['Stock'];
$object->backorder = ($data[$i]['BackorderDate'] != '' && !empty($data[$i]['BackorderDate']))?$data[$i]['BackorderDate']:NULL;
$object->modified = ($data[$i]['ModifDate'] != '' && !empty($data[$i]['ModifDate']))?$data[$i]['ModifDate']:NULL;
$object->flag = 'MODIFIED';
$object->auvibel = $auvibel->value;
$object->bebat = $bebat->value;
$object->intrastat = $intrastat;
$object->recupel = $recupel->value;
$object->reprobel = $reprobel->value;
$object->save();
$updated++;
}
elseif(($object->auvibel != $auvibel) || ($object->bebat != $bebat) || ($object->recupel != $recupel) || ($object->reprobel != $reprobel)) {
$object->auvibel = $auvibel->value;
$object->bebat = $bebat->value;
$object->intrastat = $intrastat;
$object->recupel = $recupel->value;
$object->reprobel = $reprobel->value;
$object->save();
}
}
else {
// Check if category group exists
$cat->select('id')->where(array(
'supplier_id' => $supplier->id,
'name_a' => $data[$i]['Class1'],
'name_b' => $data[$i]['Class2'],
'name_c' => $data[$i]['Class3'],
))->get(1);
if(!$cat->exists()) {
// Category should be added
$cat->supplier_id = $supplier->id;
$cat->name_a = $data[$i]['Class1'];
$cat->name_b = $data[$i]['Class2'];
$cat->name_c = $data[$i]['Class3'];
$cat->save();
// Log as notification: New supplier categorie
$this->_notify('Niewe categorie',array(
'body' => $supplier->name.' heeft "'.$cat->name_a.' - '.$cat->name_b.' - '.$cat->name_c.'" als nieuwe categorie toegevoegd.',
'controller' => 'leveranciers',
'trigger' => 'new_supplier_category',
'url' => '[hidden-url]'.$supplier->id.'/categorien',
'icon' => 'icon-truck',
'udb' => $udb,
));
}
// Check if manufacturer exists
$manu->select('id')->where(array(
'name' => $data[$i]['PublisherName']
))->get(1);
if(!$manu->exists()) {
// Manufacturer should be added
$manu->name = $data[$i]['PublisherName'];
$manu->save($supplier);
}
// Add the product to the database
$object->art_id = $data[$i]['ArtID'];
$object->supplier_id = $supplier->id;
$object->supplier_category_id = $cat->id;
$object->supplier_manufacturer_id = $manu->id;
$object->part_id = $data[$i]['PartID'];
$object->ean_code = $data[$i]['EanCode'];
$object->name = $data[$i]['Description'];
$object->description = NULL;
$object->version = (($data[$i]['Version'] != '')?$data[$i]['Version']:NULL);
$object->language = (($data[$i]['Language'] != '')?$data[$i]['Language']:NULL);
$object->media = (($data[$i]['Media'] != '')?$data[$i]['Media']:NULL);
$object->trend = (($data[$i]['Trend'] != '')?$data[$i]['Trend']:NULL);
$object->price_group = (($data[$i]['PriceGroup'] != '')?$data[$i]['PriceGroup']:NULL);
$object->price_code = (($data[$i]['PriceCode'] != '')?$data[$i]['PriceCode']:NULL);
$object->eur_lp = (($data[$i]['LP_Eur'] != '')?$data[$i]['LP_Eur']:NULL);
$object->eur_de = (($data[$i]['DE_Eur'] != '')?$data[$i]['DE_Eur']:NULL);
$object->eur_d1 = (($data[$i]['D1_Eur'] != '')?$data[$i]['D1_Eur']:NULL);
$object->eur_d2 = (($data[$i]['D2_Eur'] != '')?$data[$i]['D2_Eur']:NULL);
$object->eur_personal = $data[$i]['PricePersonal_Eur'];
$object->stock = $data[$i]['Stock'];
$object->backorder = ($data[$i]['BackorderDate'] != '' && !empty($data[$i]['BackorderDate']))?$data[$i]['BackorderDate']:NULL;
$object->modified = ($data[$i]['ModifDate'] != '' && !empty($data[$i]['ModifDate']))?$data[$i]['ModifDate']:NULL;
$object->flag = NULL;
$object->auvibel = $auvibel->value;
$object->bebat = $bebat->value;
$object->intrastat = $intrastat;
$object->recupel = $recupel->value;
$object->reprobel = $reprobel->value;
$object->save();
//$object->clear_cache();
$new++;
}
//$message .= 'loop end A: '.memory_get_usage().' - '.$i."\r\n";
$object->clear();
$cat->clear();
$manu->clear();
$auvibel->clear();
$bebat->clear();
$recupel->clear();
$reprobel->clear();
unset($data[$i]);
//$message .= 'loop end B: '.memory_get_usage()."\r\n";
}
}
unset($manu);
unset($auvibel);
unset($bebat);
unset($recupel);
unset($reprobel);
if(is_file($file)) {
unlink($file);
}
$object->clear();
//$message .= 'BEFORE MARK EOL: '.memory_get_usage()."\r\n";
/**
* Mark products as EOL when not found in file
*/
$eolCount = 0;
$eol = $object
->group_start()
->where('flag IS NULL')
->or_where('flag !=', 'EOL')
->group_end()
->where('supplier_id', $supplier->id)
->group_start()
->group_start()->where('updated IS NOT NULL')->where('updated <',$cronStart)->group_end()
->or_group_start()->where('updated IS NULL')->where('created <',$cronStart)->group_end()
->group_end()
->get_iterated();
$p = new Product(NULL,$udb);
//unset($aAvailable);
foreach($eol as $i => $product) {
$product->flag = "EOL";
$product->save();
if($product->art_id != NULL) {
// The 'copied' products should be marked eol in the webshop!
$p->where('art_code',$product->art_id)->where('supplier_product_id', $product->id)->get();
if($p->exists()) {
$p->eol = date('Y-m-d H:i:s');
$p->save();
}
$p->clear();
}
$product->clear();
$eolCount++;
//unset($eol[$i]);
//$message .= 'INSIDE MARK EOL: '.memory_get_usage()."\r\n";
}
unset($product);
$object->clear();
//$message .= 'AFTER MARK EOL: '.memory_get_usage()."\r\n";
if($eolCount > 0) {
// Log as notification: supplier products marked EOL
$this->_notify('EOL melding',array(
'body' => "Er ".(($eolCount == 1)?'is een product':'zijn '.$eolCount.' producten')." gemarkeerd als EOL",
'controller' => 'leveranciers',
'trigger' => 'eol_supplier_product',
'url' => '[hidden-url]'.$supplier->id.'/artikels',
'icon' => 'icon-truck',
'udb' => $udb,
));
}
}
// After looping files build e-mail.
$message .= 'Totaal: '.$rows. "\r\n";
$message .= 'new: '.$new. "\r\n";
$message .= 'updated: '.$updated. "\r\n";
$message .= 'EOL: '.$eolCount. "\r\n";
$subject = 'Import XXXXX Update';
}
// No updates found
else {
$subject = 'Import XXXXX No Update Found';
$message .= "\r\n";
}
$message .= '<h3>Einde: '.date('Y-m-d H:i:s').'</h3>' . "\r\n";
mail($this->adminMail, $subject, $message, $this->headerMail);
// Remove import_found marker for supplier
$supplier->import_found = false;
$supplier->save();
We had a similar situation. After a lot of attempts at making the script better, we decided that we needed another approach to make our import work and not take ~10 hours.
What we did was dump all the PHP code, and instead use mysqlimport to load the contents of the CSV file directly into a table. That table now contains everything we need, but not in a form that's useful for us (no structure, some fields need some processing, etc.)
However, because everything is now in the database, we can do everything we want with a query.
For example, deleting all data that is no longer in the import file, thats just DELETE FROM structured_table AS st LEFT JOIN unstructured_table AS ut ON st.someField = ut.someField WHERE ut.someField IS NULL;, updating existing records is just UPDATE structured_table AS st INNER JOIN unstructured_table AS ut ON st.someField = ut.someField SET st.anotherField = CONCAT(ut.aField, ' ', ut.yetAnotherField);.
Obviously, for a complex import script, your queries will be more complex and you'll need more of them. You might even need to throw some stored procedures in to do processing on individual fields. But if you can take this kind of approach you'll end up with a process that can handle a lot of data and is very scalable.
I have a similar situation... Compare around 20M records every day to update a few records with changes and add / remove the delta. Data source is CSV as well. I use perl, while I think php also work.
Each record must have a linking key, SKU of product? Or something like that. May already be the primary key /unique key in your DB table.
You know the lst of fields that you want to compare and update.
Step 1: read ALL records from DB, store in an array using the linking key as named index.
1.1: value is concat of all fields need to compare, or md5() of the concat result to save memory.
Step 2: loop through the CSV file, extract the linking key and new values per row.
2.1: if linking key is NOT in the array, INSERT action to DB.
2.2: isset() return true so compare the values (or md5() of the value concat), if different, UPDATE action to DB.
2.3: delete this entry from the array.
Step 3: by the end of reading the CSV, the entries remains in the array were records to DELETE.
In my case, it use less than 2GB RAM for the process and runs around 3 minutes, which should be feasible and acceptable.

Correction function calculation in your online ratings

I need your help on this php function.
The function takes the data from the db, does the account of the score and exports it. In practice the score must be calculated for both the buyer and for the seller ($type) but when I go to export I only have one of the buyers. The code in question is below. Thanks in advance for the help.
function shop_get_ratings($user_id, $type = 'seller'){
$type = strtolower($type);
$valid = array('seller','buyer');
if( !in_array($type, $valid)){
return false;
}
$conn = getConnection();
$sql = 'SELECT AVG(i_%s_score) as %s_rating FROM %st_shop_transactions WHERE fk_i_user_id = %d AND i_%s_score IS NOT NULL';
$rs = $conn->osc_dbFetchResults(sprintf($sql,$type,$type, DB_TABLE_PREFIX, $user_id, $type));
$seller_r = 0;
if( false !== $rs && isset($rs[0]['seller_rating']) && !is_null($rs[0]['seller_rating']) ){
$seller_r = (int)$rs[0]['seller_rating'];
}
$sql = 'SELECT COUNT(*) as rating_count FROM %st_shop_transactions WHERE fk_i_user_id = %d AND i_%s_score IS NOT NULL';
$rs = $conn->osc_dbFetchResults(sprintf($sql, DB_TABLE_PREFIX, $user_id, $type));
$seller_r_c = 0;
if( false !== $rs && isset($rs[0]['rating_count']) && !is_null($rs[0]['rating_count']) ){
$seller_r_c = (int)$rs[0]['rating_count'];
}
$percentage = 0;
if( $seller_r > 0 ){
$percentage =($seller_r/5)*100;
}
$stats = array(
'average_rating' => (int)$seller_r,
'rating_percentege' => (float)$percentage,
'rating_count' => (int)$seller_r_c,
);
View::newInstance()->_exportVariableToView($type.'_ratings', $stats);
return $stats;
}
From reading the code, it looks like you should get a rating for the seller ok, but it's the buyer who ends up with a 0 rating.
This is because in the line $sql = 'SELECT AVG(i_%s_score) as %s_rating you are inserting $type in to the query to have the field named seller_type or buyer_type, depending on the type of rating you're trying to get with the function.
However when querying the result set you are explicitly looking for the field named seller_rating. This field won't be set when $type is buyer, so $seller_r will always be 0.
The simplest fix here is likely to name the field as something like avg_rating in the sql, with no $type-dependent var name injection. So, something like:
$sql = 'SELECT AVG(i_%s_score) as avg_rating
FROM %st_shop_transactions
WHERE fk_i_user_id = %d
AND i_%s_score IS NOT NULL';
$rs = $conn->osc_dbFetchResults(
sprintf($sql, $type, DB_TABLE_PREFIX, $user_id, $type)
);
$seller_r = 0;
if (false !== $rs
&& isset($rs[0]['avg_rating'])
&& !is_null($rs[0]['avg_rating'])
){
$seller_r = (int)$rs[0]['avg_rating'];
}

MySQL Search from Multiple User supplied where clause - Fix and Better Algorithm

I want to run search query where i have multiple where clause. and multiple depends upon the user argument.
for example i mean, Search may depend on 1 column, 2 column, 3 column or 6 column in my case, and i don't want to run if-elseif-else statement with all column probability. So, i have just built up below function, but i am stuck with and that comes in between multiple column search case. Below is my code :-
function listPlayer($player="player_guest", $group="group_guest",
$weapon="weapon_guest", $point="point_guest", $power="level_guest",
$status="status_guest") {
$lePlayer = (isset($player) && $player != "player_guest") ?
'player= '.$mysqli->real_escape_string($player).' and' : '';
$leGroup = (isset($group) && $group != "group_guest") ?
'group= '.$mysqli->real_escape_string($group).' and' : '';
$leWeapon = (isset($weapon) && $weapon != "weapon_guest") ?
'weapon= '.$mysqli->real_escape_string($weapon).' and' : '';
$lePoint = (isset($point) && $point != "point_guest") ?
'point= '.$mysqli->real_escape_string($point).' and' : '';
$lePower = (isset($power) && $power != "level_guest") ?
'level= '.$mysqli->real_escape_string($power).' and' : '';
$leStatus = (isset($status) && $status != "status_guest") ?
'status= '.$mysqli->real_escape_string($status).' and' : '';
$query = "Select pid, name from game where {$lePlayer} {$leGroup} {$leWeapon} {$lePoint} {$lePower} {$leStatus} ";
$runQuery = $mysqli->query($query);
}
but problem is and at the end. If i use them, than i have extra and at the end, and if i don't use them that's again an error.
Can some one help me to fix and find better way to do it.
Update: My Update Code that works if some one needs them Thanks to Barmar
function listPlayer($player="player_guest", $group="group_guest",
$weapon="weapon_guest", $point="point_guest", $power="level_guest",
$status="status_guest") {
$lePlayer = (isset($player) && $player != "player_guest") ?
'player= '.$mysqli->real_escape_string($player) : '' ;
$leGroup = (isset($group) && $group != "group_guest") ?
'group= '.$mysqli->real_escape_string($group) : '' ;
$leWeapon = (isset($weapon) && $weapon != "weapon_guest") ?
'weapon= '.$mysqli->real_escape_string($weapon) : '' ;
$lePoint = (isset($point) && $point != "point_guest") ?
'point= '.$mysqli->real_escape_string($point) : '' ;
$lePower = (isset($power) && $power != "level_guest") ?
'level= '.$mysqli->real_escape_string($power) : '' ;
$leStatus = (isset($status) && $status != "status_guest") ?
'status= '.$mysqli->real_escape_string($status) : '' ;
$condition_array = ( $lePlayer , $leGroup , $leWeapon , $lePoint , $lePower , $leStatus)
$condition_stirng = implode(' and ', $condition_array);
$query = "Select pid, name from game where ".$condition_stirng;
$runQuery = $mysqli->query($query);
}
Update:
I got mail from someone at my email which says my code is vulnerable to SQL Injection. Here it is POC http://www.worldofhacker.com/2013/09/interesting-sql-vulnerable-code-even.html
Thanks
Put all the conditions in an array. Then combine them with:
$condition_string = implode(' and ', $condition_array);
The simple solution is to trim the "and" off at the end:
$query = substr($query, 0, strlen($query) - 3);
however a more efficient way would be to put them in a loop, like this:
$wheres = array("player_guest"=>$player, "group_guest"=>$group.....);
$query_where = "";
$i = 0;
foreach($wheres as $where=>$value){
list($condition, $null) = explode("_",$where);
if(isset($value)){
$query_where .= $condition . "='" . $mysqli->real_escape_string($value)."'";
if($i != sizeof($wheres)){
$query_where .= " and ";
}
}
$i++;
}
This is extendable for any number of conditions, and doesnt require the extra string function at the end.
Notice the $defaults is needed to make sure your conditions work. A bit repetitive, but it's all due to your function declaration.
function listPlayer(
$player="player_guest",
$group="group_guest",
$weapon="weapon_guest",
$point="point_guest",
$power="level_guest",
$status="status_guest") {
//I'm just copying whatever is in the default parameters ;)
$defaults = array(
'player' => 'player_guest',
'group' => 'group_guest',
'weapon' => 'weapon_guest',
'point' => 'point_guest',
'power' => 'level_guest',
'status' => 'status_guest'
);
//Set all user parameters into an array, easier to loop through
$data = compact(array_flip($defaults));
//Then we build conditions
$conditions = array();
foreach($data as $k => $v) {
if ($defaults[k] !== $v) {
$v = $mysqli->real_escape_string($v);
$conditions[] = "$k='$v'";
}
}
//And build query
$query = "SELECT pid, name FROM game WHERE ".implode(" AND ", $conditions);
$runQuery = $mysqli->query($query);
}
First of all I recommend you to look at PDO when you work with MySQL in PHP.
Such tasks are always simply solved with array maps.
function listPlayer($player = null, $group = null, $weapon = null, $point = null, $power = null, $status = null) {
$defaults = array(
'player' => 'player_guest',
'group' => 'group_guest',
'weapon' => 'weapon_guest',
'point' => 'point_guest',
'status' => 'status_guest',
);
$values = compact(array_keys($defaults));
$filtered = array_filter(array_diff_assoc($values, $defaults)); //firstly filtering out defaults, then - nulls.
$where = '';
foreach($filtered as $column => $value){
if($where){
$where .= ' AND ';
}
$where .= sprintf("`%s` = '%s'", $column, $mysqli->real_escape_string($value));
}
$query = "SELECT pid, name FROM game WHERE $where";
//executing...
}

Handling a bunch of various requests into the database

I currently have a php page that grabs information from a database and produces HTML with data attributes that are filled in by from the MySQL query. The database is going to be used to search, with many different options for searches.
What I need help with is knowing a way so to organize how the many variables are handled. It's a really big mess of code, and even with all the comments I put it gives me a headache trying to figure out how to add another variable to the search.
All the variables, except for the LIMIT to which row and how many results, are optional. So if someone leaves everything except that blank, I still want it to function as well as if they meticulously filled in all the fields.
Here's what I have, with 6 variables.
<?php
$product_size = "(".$_GET['size']." BETWEEN productsizeDOWN AND productsizeUP)"; // This code sets the variable to input into the MySQL string based on the URL
$product_size_check = $_GET['size']; // the _checks check are used to see if the value is or isn't empty using if statements below
$manufacturer = $_GET['manufacturer'];
$product_manufacterer_check = $_GET['manufacturer']; // _check
$product_invisible = "(hideproduct = '".$_GET['invisible']."')"; // Checks if product is hidden
$product_invisible_check = $_GET['invisible']; // _check
$product_instock_check = $_GET['instock']; // _check
$product_limit0 = $_GET['startat']; // This is the first number after LIMIT; the row to start in.
$product_limit1 = $_GET['results']; // This is how many results to load.
$manufacturer_array = explode(",", $manufacturer); // The manufacturer comes in as "Nike,Addidas,Rebok" and is turned into an array
$manufacturer_imploded = implode("' OR productmanufacturer = '", $manufacturer_array); // Puts it back together with "OR productmanufacturer =" between each name.
$product_manufacterer = ("(productmanufacturer = '".$manufacturer_imploded."')"); // formats it so it can be directly inserted into MySQL string with a WHERE in front.
if($product_invisible_check == ""){
$product_invisible = "";
}else{$where = "WHERE ";}; //Useless code that I havn't deleted that I tried to use when I searched the entire database
if($product_size_check == ""){
$product_size = "";
}else{$where = "WHERE ";};
if($product_manufacterer_check == ""){
$product_manufacterer = "";
}else{$where = "WHERE ";};
if($product_instock_check == "N"){
$product_instock = "(stockstatus <= '0' AND donotallowbackorders = 'Y') AND "; // Checks if product is in stock (Allowing backordering OR stock >1)
$where = "WHERE ";
}
elseif($product_instock_check == "Y") {
$product_instock = "(stockstatus > '0' OR donotallowbackorders = 'N') AND ";
$where = "WHERE ";
}
else {
$product_instock = "";
};
$sql="Select * FROM ioa7pd_Products WHERE ".$product_instock.$product_size."AND".$product_manufacterer_and.$product_manufacterer."".$product_invisible." LIMIT ".$product_limit0.", ".$product_limit1; // The end result of it all.
echo $sql;
?>
When the URL is
test.php?size=5&manufacturer=Nike,Addidas,Rebok&invisible=N&instock=Y&startat=0&results=30
the resulting SQL query is
Select * FROM ioa7pd_Products WHERE (stockstatus > '0' OR donotallowbackorders = 'N') AND (5 BETWEEN productsizeDOWN AND productsizeUP)AND(productmanufacturer = 'Nike' OR productmanufacturer = 'Addidas' OR productmanufacturer = 'Rebok')(hideproduct = 'N') LIMIT 0, 30
But I plan to add more options to the search.
My main question is simply: What way can I organize this to make it simple to add more variables? Tiered if statements?
Travesty has been helping me with my code and has really been great in organizing it.
Here is the current code. It needs to be secure to prevent injection.
// Database connection
$con = mysql_connect("[CENSORED]","[CENSORED]","[CENSORED]")
or die("Could not connect: " . mysql_error());
mysql_select_db("[CENSORED]") or die('Could not select database');
// Begin organization of URL variables into MYSQL Query
$get_size = $_GET['size'];
$get_manufacturer = $_GET['manufacturer'];
$get_invisible = $_GET['invisible'];
$get_instock = $_GET['instock'];
$get_sex = $_GET['sex'];
$get_startat = $_GET['startat'];
$get_results = $_GET['results'];
if ($get_size != ""){
$all_selectors[] = "(".$get_size." BETWEEN productsizeDOWN AND productsizeUP)"; // Add to array if size is not blank.
};
if ($get_manufacturer != ""){
$manufacturer_exploded = explode(",", $get_manufacturer);
$manufacturer_imploded = implode("' OR productmanufacturer = '", $manufacturer_exploded);
$all_selectors[] = ("(productmanufacturer = '".$manufacturer_imploded."')");
};
if ($get_invisible != ""){
$all_selectors[] = "(hideproduct = '".$get_invisible."')";
};
if($get_instock == "N" or $get_instock == "n"){
$all_selectors[] = "(stockstatus <= '0' AND donotallowbackorders = 'Y')";
}elseif($get_instock == "Y" or $get_instock == "y") {
$all_selectors[] = "(stockstatus > '0' OR donotallowbackorders = 'N')";
};
if ($get_startat != "" or $get_results != ""){
$number_results = "LIMIT ".$get_startat.", ".$get_results;
} else {
$number_results = "LIMIT 0, 15";
};
// All variables are now in an array, except "startat" and "results"
$all_selectors0 = "WHERE ".implode(" AND ", $all_selectors);
// Create SQL query
$sql="Select * FROM sadsads_Products ".$all_selectors0." ".$number_results;
I would do something more like this. It's not tested and probably not 100% complete...you may need to do some further customization, particularly with adding more special cases to the switch statement, but this will make adding more variables much easier:
REMOVED OLD EXAMPLE, SEE UPDATED EXAMPLE BELOW
One key thing to note is that you aren't sanitizing your database inputs. Your code is vulnerable to SQL injection. My example above helps to solve that, but this code isn't fully tested, so you should ensure that all user input is sanitized before using it in any query.
If your field names don't match up with your MySQL columns (which it looks like they don't), then you can fix them with an associative array:
$columns = array(
// [form field] => [mysql column]
'size' => 'product_size',
'manufacturer' => 'product_manufacturer',
'invisible' => 'hideproduct'
// ...
);
And then in your switch statement, do something more like this:
$whereClause[] = "{$columns[$key]} = '{$value}'";
FINAL UPDATE:
DOCUMENTED SAMPLE - has plenty of comments and extra stuff to make it work on Codepad
EXACT WORKING CODE - you should be able to copy and paste this (and add your DB credentials) and it should work:
$con = mysqli_connect("[CENSORED]", "[CENSORED]", "[CENSORED]") or die("Could not connect: ". mysqli_error());
mysqli_select_db("[CENSORED]") or die("Could not select database");
$columns = array(
'size' => 'product_size',
'manufacturer' => 'product_manufacturer',
'invisible' => 'hideproduct'
);
$whereClause = array();
$limit = array("startat" => 0, "results" => 15);
foreach ($_GET as $key=>$value) {
$key = mysqli_real_escape_string($key);
if (is_array($value)) {
for ($i = 0; $i < count($value); $i++) {
$value[$i] = mysqli_real_escape_string($value[$i]);
}
} else {
$value = mysqli_real_escape_string($value);
}
switch ($key) {
case 'size':
$whereClause[] = "({$value} BETWEEN productsizeDOWN AND productsizeUP)";
break;
case 'startat':
case 'results':
$limit[$key] = $value;
break;
case 'instock':
$whereClause[] = "(stockstatus ". ($value == 'N' ? "<=" : ">") ." '0' ". ($value == 'N' ? "AND" : "OR") ." donotallowbackorders = '". ($value == 'N' ? "Y" : "N") ."')";
break;
default: {
if (is_array($value)) {
$whereClause[] = "{$columns[$key]} IN ('". implode("', '", $value) ."')";
} else {
$whereClause[] = "{$columns[$key]} = '{$value}'";
}
}
}
}
$sql = "SELECT * FROM ioa7pd_Products". (empty($whereClause) ? "" : " WHERE ". implode(" AND ", $whereClause)) ." LIMIT {$limit['startat']}, {$limit['results']}";
echo $sql;
after
else {
$product_instock = "";
};
do:
$limit = '';
if( !empty($product_limit0) && !empty($product_limit1) )
$limit = " LIMIT $product_limit0, $product_limit1";
$sql="Select * FROM ioa7pd_Products WHERE ".$product_instock.$product_size."AND".$product_manufacterer_and.$product_manufacterer."".$product_invisible." $limit"; // The end result of it all.
echo $sql;
If you have separate params in $_GET, you would have to traverse with multiple if statements. you can pass the params as an array into $_GET, with numeric keys, that would help a bunch.

Cakephp custom query pagination

I'm trying to use custom query in Cake then paginate the results with the code below:
$query = $this->Petition->query($sql);
I tried:
$petitions = $this->paginate($query);
and it doesn't work. Is there a way we can do this?
OK I wasn't clear enough: I need to use variable array fetched from custom query on pagination so I can use this for pagination in the view. Is there an easy way of doing this?
Below is my code:
function index() {
if ($this->Session->read('Auth.User.group_id') != 1) {
$commune_id = $this->Session->read('Auth.User.commune_id');
$commune_id = $this->Petition->Commune->findbyId($commune_id);
$commune_id = $this->Petition->Commune->find('all',array('conditions' => array('group' => $commune_id['Commune']['group'])));
$count = count($commune_id);
$i=1;
$sql = "SELECT * FROM `petitions` WHERE `commune_id` = ";
foreach($commune_id as $commune_ids){
if($i==1){
$sql .= $commune_ids['Commune']['id'];
}else{
$sql .= " OR `commune_id` = ".$commune_ids['Commune']['id'];
}
/*if($i != $count){
$this->paginate = array(
'or' => array(
array('Petition.commune_id LIKE' => $commune_ids['Commune']['id'] . ","),
//array('Petition.commune_id LIKE' => "," . $commune_ids['Commune']['id'] . ",")
),
'limit' => 10
);
}*/
$i++;
}
$query = $this->Petition->query($sql);
}
$this->Petition->recursive = 0;
$petitions = $this->paginate();
$this->set('petitions', $petitions);
}
you seriously need to read the pagination part in the cake book:
function index() {
$conditions = array();
if ($this->Auth->user('group_id') != 1) {
$commune_id = $this->Petition->Commune->findById($this->Auth->user('commune_id'));
$conditions['Petition.id'] = $this->Petition->Commune->find('list',array(
'fields'=>array('id','id')
'conditions' => array('group' => $commune_id['Commune']['group'])
));
}
$this->Petition->recursive = 0;
$petitions = $this->paginate('Petition',$conditions);
$this->set('petitions', $petitions);
}
something like that.
this is not how pagination works
you need to fill $this->validate with your conditions etc
and then use $this->paginate() plainly
see
http://book.cakephp.org/view/1232/Controller-Setup
also note the chapter about the custom query part if it is really(!) necessary.

Categories