I use PHP to execute SQL queries and then extract timesheet data for a bunch of users.
Earlier it ran decently fast but has now slowed down drastically due to increase in number of Users and Tasks.
I have used a number of while, for loops. Not sure what I can optimize as calculations are iterative.
Appreciate if someone could look at the code and suggest some options.
private function processGETUSERTIMEREPORTDATA(SimpleXMLElement $xml, DBHandler $dbh) {
$from = Utils::convertToDatetime($this->clientDateFormat, $xml->FROM);
$to = Utils::convertToDatetime($this->clientDateFormat, $xml->TO);
$usercode = stripslashes(html_entity_decode($xml->UNCODE));
$this->logger->LogDebug("User List: " . $usercode);
$userArr = json_decode($usercode, TRUE);
$tChgMode = $xml->TASKCHGMODE;
$tType = $xml->TASKTYPE;
$timeSheetCategory = $xml->TIMESHEETCAT;
$totalChgHrsArr = array();
$totalNonChgHrsArr = array();
$totalGenNonChgHrsArr = array();
$totalChgDaysArr = array();
$totalDaysALeaveArr = array();
$totalDaysSLeaveArr = array();
$totalDaysCLeaveArr = array();
$totalExpensesArr = array();
// Set timesheet category table
if ($timeSheetCategory == "ALL") {
$tablename = array("tblChargeHours", "tblNonChargeHours", "tblFieldDays",
"tblNonChargeDays", "tblExpensesSheet", "tblGeneralNonChargeHours");
} else if ($timeSheetCategory == "Chargeable Hours") {
$tablename = array("tblChargeHours");
} else if ($timeSheetCategory == "Non Chargeable Hours") {
$tablename = array("tblNonChargeHours");
} else if ($timeSheetCategory == "Chargeable Days") {
$tablename = array("tblFieldDays");
} else if ($timeSheetCategory == "Leaves") {
$tablename = array("tblNonChargeDays");
} else if ($timeSheetCategory == "Expenses") {
$tablename = array("tblExpensesSheet");
} else if ($timeSheetCategory == "General Non Chargeable Hours") {
$tablename = array("tblGeneralNonChargeHours");
}
$arrlength = count($tablename);
$userCount = count($userArr);
for ($i = 0; $i < $userCount; $i++) {
for ($x = 0; $x < $arrlength; $x++) {
// Leaves table
if ($tablename[$x] == "tblNonChargeDays") {
// Annual Leave
$daysSum = 0;
$query = "SELECT Mo, Tu, We, Th, Fr, Sa, Su from " . $tablename[$x] . " WHERE (WeekEnding BETWEEN '$from' AND '$to') AND (TaskCode='1') AND (NameCode='" . $userArr[$i] . "')";
$hoursRes = $dbh->executeQuery($query);
if ($dbh->getQueryStatus()) {
while ($timerow = mysqli_fetch_assoc($hoursRes)) {
$daysSum += ($timerow['Mo'] + $timerow['Tu'] + $timerow['We'] + $timerow['Th'] + $timerow['Fr'] + $timerow['Sa'] + $timerow['Su']);
}
$totalDaysALeaveArr[] = $daysSum;
} else {
$this->logger->LogError("Unable to generate Timesheet report data. Q : " . $query . " E : " . $dbh->getQueryMessage());
return ResponseGenerator::generateNegativeRepsonse($this->reqName, "Unable to generate Timesheet report data. Q : " . $query . " E : " . $dbh->getQueryMessage());
}
// Sick Leave
$daysSum = 0;
$query = "SELECT Mo, Tu, We, Th, Fr, Sa, Su from " . $tablename[$x] . " WHERE (WeekEnding BETWEEN '$from' AND '$to') AND (TaskCode='2') AND (NameCode='" . $userArr[$i] . "')";
$hoursRes = $dbh->executeQuery($query);
if ($dbh->getQueryStatus()) {
while ($timerow = mysqli_fetch_assoc($hoursRes)) {
$daysSum += ($timerow['Mo'] + $timerow['Tu'] + $timerow['We'] + $timerow['Th'] + $timerow['Fr'] + $timerow['Sa'] + $timerow['Su']);
}
$totalDaysSLeaveArr[] = $daysSum;
} else {
$this->logger->LogError("Unable to generate Timesheet report data. Q : " . $query . " E : " . $dbh->getQueryMessage());
return ResponseGenerator::generateNegativeRepsonse($this->reqName, "Unable to generate Timesheet report data. Q : " . $query . " E : " . $dbh->getQueryMessage());
}
// Compassionate Leave
$daysSum = 0;
$query = "SELECT Mo, Tu, We, Th, Fr, Sa, Su from " . $tablename[$x] . " WHERE (WeekEnding BETWEEN '$from' AND '$to') AND (TaskCode='3') AND (NameCode='" . $userArr[$i] . "')";
$hoursRes = $dbh->executeQuery($query);
if ($dbh->getQueryStatus()) {
while ($timerow = mysqli_fetch_assoc($hoursRes)) {
$daysSum += ($timerow['Mo'] + $timerow['Tu'] + $timerow['We'] + $timerow['Th'] + $timerow['Fr'] + $timerow['Sa'] + $timerow['Su']);
}
$totalDaysCLeaveArr[] = $daysSum;
} else {
$this->logger->LogError("Unable to generate Timesheet report data. Q : " . $query . " E : " . $dbh->getQueryMessage());
return ResponseGenerator::generateNegativeRepsonse($this->reqName, "Unable to generate Timesheet report data. Q : " . $query . " E : " . $dbh->getQueryMessage());
}
// GeneralNonChgHours Table
} else if ($tablename[$x] == "tblGeneralNonChargeHours") {
$hoursSum = 0;
$query = "SELECT Mo, Tu, We, Th, Fr, Sa, Su from " . $tablename[$x] . " WHERE (WeekEnding BETWEEN '$from' AND '$to') AND (NameCode='" . $userArr[$i] . "')";
$hoursRes = $dbh->executeQuery($query);
if ($dbh->getQueryStatus()) {
while ($timerow = mysqli_fetch_assoc($hoursRes)) {
$hoursSum += ($timerow['Mo'] + $timerow['Tu'] + $timerow['We'] + $timerow['Th'] + $timerow['Fr'] + $timerow['Sa'] + $timerow['Su']);
}
$totalGenNonChgHrsArr[] = $hoursSum;
} else {
$this->logger->LogError("Unable to generate Timesheet report data. Q : " . $query . " E : " . $dbh->getQueryMessage());
return ResponseGenerator::generateNegativeRepsonse($this->reqName, "Unable to generate Timesheet report data. Q : " . $query . " E : " . $dbh->getQueryMessage());
}
}
} // end-for-tablename
} // end-for-usercode
if ($tChgMode == "ALL") {
if ($tType == "ALL") {
// $tChgMode = ALL && $tType = ALL
$tquery = "SELECT TCode, TType FROM tblTasks WHERE (TCreatedDate BETWEEN '$from' AND '$to')";
} else {
// $tChgMode = ALL && $tType = Sel
$tquery = "SELECT TCode, TType FROM tblTasks WHERE (TCreatedDate BETWEEN '$from' AND '$to') AND (TType='" . $tType . "') ORDER BY TCode ASC";
}
} else {
if ($tType == "ALL") {
// $tChgMode = Sel && $tType = ALL
$tquery = "SELECT TCode, TType FROM tblTasks WHERE (TCreatedDate BETWEEN '$from' AND '$to') AND (TChargeMode='" . $tChgMode . "') ORDER BY TCode ASC";
} else {
// $tChgMode = Sel && $tType = Sel
$tquery = "SELECT TCode, TType FROM tblTasks WHERE (TCreatedDate BETWEEN '$from' AND '$to') AND (TChargeMode='" . $tChgMode . "') AND (TType='" . $tType . "') ORDER BY TCode ASC";
}
}
$result = $dbh->executeQuery($tquery);
$tqueryArr = array(); // Store the query results
while ($row = mysqli_fetch_assoc($result)) {
$tqueryArr[] = $row;
}
for ($i = 0; $i < $userCount; $i++) {
$totalExpenses = 0;
$totalChgHrs = 0;
$totalNonChgHrs = 0;
$totalChgDays = 0;
foreach ($tqueryArr as $row) {
for ($x = 0; $x < $arrlength; $x++) {
$tCode = $row['TCode'];
// Expenses Table
if ($tablename[$x] == "tblExpensesSheet") {
$query = "SELECT FinalAmount from " . $tablename[$x] . " WHERE (WeekEnding BETWEEN '$from' AND '$to') AND (TaskCode='" . $tCode . "') AND (NameCode='" . $userArr[$i] . "')";
// $this->logger->LogDebug($query);
$hoursRes = $dbh->executeQuery($query);
if ($dbh->getQueryStatus()) {
while ($exprow = mysqli_fetch_assoc($hoursRes)) {
$totalExpenses += $exprow['FinalAmount'];
}
} else {
$this->logger->LogError("Unable to generate Timesheet report data. Q : " . $query . " E : " . $dbh->getQueryMessage());
return ResponseGenerator::generateNegativeRepsonse($this->reqName, "Unable to generate Timesheet report data. Q : " . $query . " E : " . $dbh->getQueryMessage());
}
// Other Tables
} else {
$query = "SELECT Mo, Tu, We, Th, Fr, Sa, Su from " . $tablename[$x] . " WHERE (WeekEnding BETWEEN '$from' AND '$to') AND (TaskCode='" . $tCode . "') AND (NameCode='" . $userArr[$i] . "')";
$hoursRes = $dbh->executeQuery($query);
$totalHours = 0;
if ($dbh->getQueryStatus()) {
while ($timerow = mysqli_fetch_assoc($hoursRes)) {
$hoursSum = ($timerow['Mo'] + $timerow['Tu'] + $timerow['We'] + $timerow['Th'] + $timerow['Fr'] + $timerow['Sa'] + $timerow['Su']);
$totalHours += $hoursSum;
}
switch ($tablename[$x]) {
case "tblChargeHours":
$totalChgHrs += $totalHours;
break;
case "tblNonChargeHours":
$totalNonChgHrs += $totalHours;
break;
case "tblFieldDays":
$totalChgDays += $totalHours;
break;
default:
}
} else {
$this->logger->LogError("Unable to generate Timesheet report data. Q : " . $query . " E : " . $dbh->getQueryMessage());
return ResponseGenerator::generateNegativeRepsonse($this->reqName, "Unable to generate Timesheet report data. Q : " . $query . " E : " . $dbh->getQueryMessage());
}
}
} // end-for
} // end-while
$totalExpensesArr[] = $totalExpenses;
$totalChgHrsArr[] = $totalChgHrs;
$totalNonChgHrsArr[] = $totalNonChgHrs;
$totalChgDaysArr[] = $totalChgDays;
} // end-for-usercode
$xmlop = new SimpleXMLElement('<RESPONSE/>');
$xmlop->addChild("REQUESTNAME", $this->reqName);
$xmlop->addChild("REQUESTSTATUS", "SUCCESS");
$rowxml = $xmlop->addChild("ROW");
$rowxml->addChild("USERCODE", json_encode($userArr));
$rowxml->addChild("CHGHOURS", json_encode($totalChgHrsArr));
$rowxml->addChild("NONCHGHOURS", json_encode($totalNonChgHrsArr));
$rowxml->addChild("GENNONCHGHOURS", json_encode($totalGenNonChgHrsArr));
$rowxml->addChild("FIELDDAYS", json_encode($totalChgDaysArr));
$rowxml->addChild("ANNLEAVE", json_encode($totalDaysALeaveArr));
$rowxml->addChild("SICKLEAVE", json_encode($totalDaysSLeaveArr));
$rowxml->addChild("COMPLEAVE", json_encode($totalDaysCLeaveArr));
$rowxml->addChild("EXPENSES", json_encode($totalExpensesArr));
return $xmlop->asXML();
}
Apart from the complicated code, you will probably find that you have an indexing (or lack of) issue.
Find every field that is used in any of the WHERE clauses and create an index for it.
It's quite possible that if you have a growing database that is gradually getting slower, it could be an indexing problem.
Related
I try to inject some optional SQL to prepared statement with the parameter $and:
public function loadInfoAndStatus($property_id, $property_item_type_id, $and, $returnArray = false)
{
if (!isset($property_id) || empty($property_id)
|| !isset($property_item_type_id) || empty($property_item_type_id)
|| !isset($and) || empty($and)) {
error_log(get_class() . " - " . __FUNCTION__ ." : required params not set or empty");
return false;
}
$sql = " SELECT pi.status, pi.info, pi.property_item_id "
. " FROM ". self::TABLE ." pi "
. " JOIN countries c ON c.country_id = pi.country_id "
. " WHERE pi.property_id = ? "
. " AND property_item_type_id = ? "
. $this->con->real_escape_string($and) // <--- here
. " ORDER BY pi.status "
. " DESC LIMIT 0,1";
$err = "";
if (!$stmt = $this->con->prepare($sql)) {
$err .= "Prepare failed: (" . $this->con->errno . ") " . $this->con->error;
}
...
But if I call the function e.g.
$row2 = Main::getModel("Property/Item")->loadInfoAndStatus(
$id
, $property_item_type_id
, " AND c.iso = 'DE' "
, true
);
Hint: $and can be one of:
" AND c.iso <> 'DE' AND c.european <> 1 "
" AND c.iso <> 'DE' AND c.european = 1 "
" AND c.iso = 'DE' "
Then I get "Prepare failed" but there is no error message.
Resulting SQL:
SELECT pi.status, pi.info, pi.property_item_id FROM property_item pi JOIN countries c ON c.country_id = pi.country_id WHERE pi.property_id = ? AND property_item_type_id = ? AND c.iso = \'DE\' ORDER BY pi.status DESC LIMIT 0,1
It works if I don't use real_escape_string
Do I have to create new functions for each new sql, or is there another way?
You have to list all possible variants in your function.
This is a toilsome task but you have to realize that's the only way.
public function loadInfoAndStatus($property_id, $property_item_type_id, $iso = null, $european = null, $returnArray = false)
{
if (empty($property_id) || empty($property_item_type_id)) {
error_log(get_class() . " - " . __FUNCTION__ ." : required params not set or empty");
return false;
}
$parameters = [$property_id, $property_item_type_id];
$sql = " SELECT pi.status, pi.info, pi.property_item_id "
. " FROM ". self::TABLE ." pi "
. " JOIN countries c ON c.country_id = pi.country_id "
. " WHERE pi.property_id = ? "
. " AND property_item_type_id = ? ";
if ($iso) {
$sql .= " AND c.iso <> ? ";
$parameters[] = $iso;
}
if ($european === true) {
$sql .= " AND c.european == 1 ";
} elseif ($european === false) {
$sql .= " AND c.european <> 1 ";
}
$sql .= " ORDER BY pi.status ";
$sql .= " DESC LIMIT 0,1";
$stmt = $this->con->prepare($sql);
$stmt->bind_param(str_repeat("s", count($parameters)), ...$parameters);
$stmt->execute();
I also removed some cargo cult code from your method, in case you are interested why
Do you really need to check for both isset() and empty() at the same time?
PHP error reporting
I solved the problem by using a whitelist method:
public function loadInfoAndStatus($property_id, $property_item_type_id, $and = "", $returnArray = false)
{
if (empty($property_id) || empty($property_item_type_id) || empty($and)) {
error_log(get_class() . " - " . __FUNCTION__ ." : required params not set or empty");
return false;
}
if (!$this->isSqlInWhitelist($and, array(
"AND c.iso = 'DE'"
,"AND c.iso <> 'DE' AND c.european = 1"
,"AND c.iso <> 'DE' AND c.european <> 1"
))) {
error_log(get_class() . " - " . __FUNCTION__ ." : sql is not in whitelist.");
return false;
}
$sql = " SELECT pi.status, pi.info, pi.property_item_id "
. " FROM ". self::TABLE ." pi "
. " JOIN countries c ON c.country_id = pi.country_id "
. " WHERE pi.property_id = ? "
. " AND property_item_type_id = ? "
. $and
. " ORDER BY pi.status "
. " DESC LIMIT 0,1";
$stmt = $this->con->prepare($sql);
...
...
protected function isSqlInWhitelist($sql, $whitelist)
{
if (!empty($sql)) {
if (!in_array(trim($sql), $whitelist)) { return false; }
}
return true;
}
everyone!
I have 3 tables:
order_product, product and product.
I have this function:
public function getOrders($data = array()) {
$sql = "SELECT MIN(o.date_added) AS date_start, MAX(o.date_added) AS date_end, COUNT(*) AS `orders`, SUM((SELECT SUM(op.quantity) FROM `" . DB_PREFIX . "order_product` op WHERE op.order_id = o.order_id GROUP BY op.order_id)) AS products, SUM((SELECT SUM(ot.value) FROM `" . DB_PREFIX . "order_total` ot WHERE ot.order_id = o.order_id AND ot.code = 'tax' GROUP BY ot.order_id)) AS tax, SUM((SELECT SUM(ot.value) FROM `" . DB_PREFIX . "order_total` ot WHERE ot.order_id = o.order_id AND ot.code = 'shipping' GROUP BY ot.order_id)) AS shipping_fee ,SUM(o.total) AS `total` FROM `" . DB_PREFIX . "order` o";
if (!empty($data['filter_order_status_id'])) {
$sql .= " WHERE o.order_status_id = '" . (int)$data['filter_order_status_id'] . "'";
} else {
$sql .= " WHERE o.order_status_id > '0'";
}
if (!empty($data['filter_date_start'])) {
$sql .= " AND DATE(o.date_added) >= '" . $this->db->escape($data['filter_date_start']) . "'";
}
if (!empty($data['filter_date_end'])) {
$sql .= " AND DATE(o.date_added) <= '" . $this->db->escape($data['filter_date_end']) . "'";
}
if (!empty($data['filter_group'])) {
$group = $data['filter_group'];
} else {
$group = 'week';
}
switch($group) {
case 'day';
$sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added), DAY(o.date_added)";
break;
default:
case 'week':
$sql .= " GROUP BY YEAR(o.date_added), WEEK(o.date_added)";
break;
case 'month':
$sql .= " GROUP BY YEAR(o.date_added), MONTH(o.date_added)";
break;
case 'year':
$sql .= " GROUP BY YEAR(o.date_added)";
break;
}
$sql .= " ORDER BY o.date_added DESC";
if (isset($data['start']) || isset($data['limit'])) {
if ($data['start'] < 0) {
$data['start'] = 0;
}
if ($data['limit'] < 1) {
$data['limit'] = 20;
}
$sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
}
$query = $this->db->query($sql);
return $query->rows;
}
I have a report page, where the results are groupd by day/month or week, depending on the switch value ( as seen in the code ).
What I need is to add a custom field "REP" which will sum the value of the rows under "REP_VALUE" in PRODUCT table.
I have REP_VALUE in PRODUCT table.
So the final need is to add this SUM value of REP_VALUE under the reports.
I tried a few things, but it seems to be messed up, the code doesn't return proper things.
Here was my attempt, I added the following code inside the $sql query:
SUM((SELECT
SUM(myma_product.rep_value)
FROM
myma_product,
myma_order_product
WHERE
myma_product.product_id = myma_order_product.product_id)) as rep_values
But the result wasn't that I was looking for and the results were incorrect.
Tried a few other request but still unsuccessfully.
I have this .Net REST API function modified from the demo for a grid control here: http://gijgo.com/grid/demos/ajax-sourced-data.
However, I am using PHP on the backend.
public JsonResult Get(int? page, int? limit)
{
List<Models.inventory> records;
int total;
using (ApplicationDbContext context = new ApplicationDbContext())
{
var query = context.inventory.Select(p => new Models.inventory
{
id = p.id,
company = p.company,
part = p.part,
year = p.year,
model = p.model,
stock = p.stock,
ic = p.ic,
vin = p.vin,
status = p.status,
bodycolor = p.bodycolor,
condition = p.condition,
comments = p.comments,
miles = p.miles,
price = p.price,
qty = p.qty
});
query = query.OrderBy(q => q.id);
total = query.Count();
if (page.HasValue && limit.HasValue)
{
int start = (page.Value - 1) * limit.Value;
records = query.Skip(start).Take(limit.Value).ToList();
}
else
{
records = query.ToList();
}
}
return this.Json(new { records, total }, JsonRequestBehavior.AllowGet);
}
I have converted the function to PHP but am having difficulty with the pagination because the total records isn't really being sent.
The C# code, filters the data after the query and i'm not sure how to do that with PHP and PostgreSQL.
I also want to return the total number of rows without the limit and offset in the result I suppose I'm going to have to run a 2nd query to return that data,
is that correct or can I do it in another way?
private function GetInventory()
{
$ic=$_request['ic'];
$limit=$_request['limit'];
$offset=$_request['page']-1;
$sql = "SELECT u.id, "
. " (SELECT company FROM urgss.users WHERE id = u.id) AS company, "
. " u.itemname || ' (' || u.part || ')' as part, "
. " u.year, "
. " u.model, "
. " u.stockno AS stock, "
. " u.ic AS ic, "
. " u.vin, "
. " u.status || ' / ' || u.currentstatus AS status, "
. " REGEXP_REPLACE(u.bod_col, '[\[\]]', '', 'g') AS bodycolor, "
. " u.condition, "
. " u.comments, "
. " CASE "
. " WHEN u.miles::integer < 1000 THEN "
. " u.miles::INTEGER * 1000 "
. " ELSE "
. " u.miles::INTEGER "
. " END AS miles, "
. " CASE "
. " WHEN u.rprice > 0 THEN "
. " u.rprice "
. " ELSE "
. " NULL "
. " END AS price, "
. " u.qty "
. "FROM unet u "
. "WHERE ic = '" . $ic. "' "
. "AND year::integer > 0"
. " LIMIT " . $limit . " OFFSET ". ($offset * $limit);
$result = pg_exec($db, $sql);
$rows = pg_fetch_all($result);
echo json_encode($rows);
}
If you remove LIMIT from the query (REMOVE THIS . " LIMIT " . $limit . " OFFSET ". ($offset * $limit);), then there are two options:
Instead of pg_fetch_all you could fetch in a loop and specify the $row_num based on offset and limit:
//get total number of rows
$total = pg_num_rows($result);
//get rows from offset up to limit
$row_num = $offset;
while($row_num < ($offset + $limit) && $rows[] = pg_fetch_assoc($result, $row_num)) {
$row_num++;
}
echo json_encode($rows);
Or pg_fetch_all and slice what you want:
//get total number of rows
$total = pg_num_rows($result);
//get ALL rows
$rows = pg_fetch_all($result);
//get rows from offset up to limit
$rows = array_slice($rows, $offset, $limit);
echo json_encode($rows);
Not sure what new { records, total } does in .NET, but you could return one of these:
return json_encode([$rows, $total]);
//or
return json_encode(['rows' => $rows, 'total' => $total]);
I want to auto numbering but by user group
Query :
$query = "select distinct(DATE_FORMAT(a.in_time, '%Y%m%d')) as in_time, a.user_id, a.notes, b.nama from attendance a left join dat_login b on(a.user_id=b.ID) where DATE_FORMAT(a.in_time, '%Y')='2017' and DATE_FORMAT(a.in_time, '%M')='April' and id_shop!=2 order by b.nama asc";
$querynm = "select distinct(b.nama) as nama from attendance a left join dat_login b on(a.user_id=b.ID) where DATE_FORMAT(a.in_time, '%Y')='2017' and DATE_FORMAT(a.in_time, '%M')='April' group by b.nama order by b.nama asc";
$stid = mysqli_query($conn, $query) or die (mysqli_error($conn));
$stnm = mysqli_query($conn, $querynm) or die (mysqli_error($conn));
Result :
$alphabet_start = 'A';
while ($data = mysqli_fetch_assoc($stid)) {
$data['user_id'] += 1;
$no_cell = 11;
echo $data['nama'] . " ";
echo $data['user_id'] . " ";
if ($data['notes']=="") {
echo chr(ord("A") + date('d', strtotime($data['in_time']))) . $no_cell . " - " . chr(ord("A") + date('d', strtotime($data['in_time']))) . ". " . "Masuk <br>";
} else if ($data['notes']=="S") {
echo chr(ord("A") + date('d', strtotime($data['in_time']))) . $no_cell . " - " . chr(ord("A") + date('d', strtotime($data['in_time']))) . ". " . "Sakit <br>";
} else if ($data['notes']=="L") {
echo chr(ord("A") + date('d', strtotime($data['in_time']))) . $no_cell . " - " . chr(ord("A") + date('d', strtotime($data['in_time']))) . ". " . "Libur <br>";
}
$alphabet_start++;
$no_cell++;
$data['user_id']++;
}
But, the value showing like this :
I want the value showing like this :
As you can see the number is changing in the next name, how to do it ?
Sorry for my bad grammar. Thanks before :)
Your code is a bit confusing but I think I understand what you mean.
You can do it as such:
$alphabet_start = 'A';
$userID = "";
$cells = array();
$cellNum = 11;
while ($data = mysqli_fetch_assoc($stid)) {
$data['user_id'] += 1;
if (in_array($data['user_id'], $cells)) {
$no_cell = $cellNum;
} else {
$no_cell = ($cellNum += 1);
$cells[] = $data['user_id'];
}
echo $data['nama'] . " ";
echo $data['user_id'] . " ";
if ($data['notes'] == "") {
echo chr(ord("A") + date('d', strtotime($data['in_time']))) . $no_cell . " - " . chr(ord("A") + date('d', strtotime($data['in_time']))) . ". " . "Masuk <br>";
} else if ($data['notes'] == "S") {
echo chr(ord("A") + date('d', strtotime($data['in_time']))) . $no_cell . " - " . chr(ord("A") + date('d', strtotime($data['in_time']))) . ". " . "Sakit <br>";
} else if ($data['notes'] == "L") {
echo chr(ord("A") + date('d', strtotime($data['in_time']))) . $no_cell . " - " . chr(ord("A") + date('d', strtotime($data['in_time']))) . ". " . "Libur <br>";
}
$alphabet_start++;
$no_cell++;
$data['user_id']++;
}
I have implemented jsTree on my site with a php/MySQL back-end for tree storage and retrieval. I used the php/MySQL demo that came with the jsTree download for the basic infrastructure and then modified to my needs.
I have modified so that multiple trees can be stored in the same database, and added a new column of "owner_id" that stores the userid of the person that created that particular tree.
The php code that creates a new branch or moves a branch is not working correctly as it is not taking into account that there are multiple trees within the database.
jsTree uses the nested set model, and the script is adjusting the left and right values of all the trees in the database instead of just the one that has had a new branch added. This is slowly corrupting the entire database.
The following code shows the function/s that does the adjusting, could someone please try and amend the code for me so it uses the "owner_id" field to only make the changes to a particular tree?
function _create($parent, $position) {
return $this->_move(0, $parent, $position);
}
and then...
function _move($id, $ref_id, $position = 0, $is_copy = false) {
$hbhbhbh = fSession::get('nodes_allowed[nodes_access]');
if ($hbhbhbh == "0" || $hbhbhbh == "2" || $hbhbhbh == "3") {
if((int)$ref_id === 0 || (int)$id === 1) { return false; }
$sql = array(); // Queries executed at the end
$node = $this->_get_node_ifuueuwyhddd($id); // Node data
$nchildren = $this->_get_children($id); // Node children
$ref_node = $this->_get_node_ifuueuwyhddd($ref_id); // Ref node data
$rchildren = $this->_get_children($ref_id);// Ref node children
$ndif = 2;
$node_ids = array(-1);
if($node !== false) {
$node_ids = array_keys($this->_get_children($id, true));
// TODO: should be !$is_copy && , but if copied to self - screws some right indexes
if(in_array($ref_id, $node_ids)) return false;
$ndif = $node[$this->fields["right"]] - $node[$this->fields["left"]] + 1;
}
if($position >= count($rchildren)) {
$position = count($rchildren);
}
// Not creating or copying - old parent is cleaned
if($node !== false && $is_copy == false) {
$sql[] = "" .
"UPDATE `".$this->table."` " .
"SET `".$this->fields["position"]."` = `".$this->fields["position"]."` - 1 " .
"WHERE " .
"`".$this->fields["parent_id"]."` = ".$node[$this->fields["parent_id"]]." AND " .
"`".$this->fields["position"]."` > ".$node[$this->fields["position"]];
$sql[] = "" .
"UPDATE `".$this->table."` " .
"SET `".$this->fields["left"]."` = `".$this->fields["left"]."` - ".$ndif." " .
"WHERE `".$this->fields["left"]."` > ".$node[$this->fields["right"]];
$sql[] = "" .
"UPDATE `".$this->table."` " .
"SET `".$this->fields["right"]."` = `".$this->fields["right"]."` - ".$ndif." " .
"WHERE " .
"`".$this->fields["right"]."` > ".$node[$this->fields["left"]]." AND " .
"`".$this->fields["id"]."` NOT IN (".implode(",", $node_ids).") ";
}
// Preparing new parent
$sql[] = "" .
"UPDATE `".$this->table."` " .
"SET `".$this->fields["position"]."` = `".$this->fields["position"]."` + 1 " .
"WHERE " .
"`".$this->fields["parent_id"]."` = ".$ref_id." AND " .
"`".$this->fields["position"]."` >= ".$position." " .
( $is_copy ? "" : " AND `".$this->fields["id"]."` NOT IN (".implode(",", $node_ids).") ");
$ref_ind = $ref_id === 0 ? (int)$rchildren[count($rchildren) - 1][$this->fields["right"]] + 1 : (int)$ref_node[$this->fields["right"]];
$ref_ind = max($ref_ind, 1);
$self = ($node !== false && !$is_copy && (int)$node[$this->fields["parent_id"]] == $ref_id && $position > $node[$this->fields["position"]]) ? 1 : 0;
foreach($rchildren as $k => $v) {
if($v[$this->fields["position"]] - $self == $position) {
$ref_ind = (int)$v[$this->fields["left"]];
break;
}
}
if($node !== false && !$is_copy && $node[$this->fields["left"]] < $ref_ind) {
$ref_ind -= $ndif;
}
$sql[] = "" .
"UPDATE `".$this->table."` " .
"SET `".$this->fields["left"]."` = `".$this->fields["left"]."` + ".$ndif." " .
"WHERE " .
"`".$this->fields["left"]."` >= ".$ref_ind." " .
( $is_copy ? "" : " AND `".$this->fields["id"]."` NOT IN (".implode(",", $node_ids).") ");
$sql[] = "" .
"UPDATE `".$this->table."` " .
"SET `".$this->fields["right"]."` = `".$this->fields["right"]."` + ".$ndif." " .
"WHERE " .
"`".$this->fields["right"]."` >= ".$ref_ind." " .
( $is_copy ? "" : " AND `".$this->fields["id"]."` NOT IN (".implode(",", $node_ids).") ");
$ldif = $ref_id == 0 ? 0 : $ref_node[$this->fields["level"]] + 1;
$idif = $ref_ind;
if($node !== false) {
$ldif = $node[$this->fields["level"]] - ($ref_node[$this->fields["level"]] + 1);
$idif = $node[$this->fields["left"]] - $ref_ind;
if($is_copy) {
$sql[] = "" .
"INSERT INTO `".$this->table."` (" .
"`".$this->fields["parent_id"]."`, " .
"`".$this->fields["position"]."`, " .
"`".$this->fields["left"]."`, " .
"`".$this->fields["right"]."`, " .
"`".$this->fields["level"]."`" .
") " .
"SELECT " .
"".$ref_id.", " .
"`".$this->fields["position"]."`, " .
"`".$this->fields["left"]."` - (".($idif + ($node[$this->fields["left"]] >= $ref_ind ? $ndif : 0))."), " .
"`".$this->fields["right"]."` - (".($idif + ($node[$this->fields["left"]] >= $ref_ind ? $ndif : 0))."), " .
"`".$this->fields["level"]."` - (".$ldif.") " .
"FROM `".$this->table."` " .
"WHERE " .
"`".$this->fields["id"]."` IN (".implode(",", $node_ids).") " .
"ORDER BY `".$this->fields["level"]."` ASC";
}
else {
$sql[] = "" .
"UPDATE `".$this->table."` SET " .
"`".$this->fields["parent_id"]."` = ".$ref_id.", " .
"`".$this->fields["position"]."` = ".$position." " .
"WHERE " .
"`".$this->fields["id"]."` = ".$id;
$sql[] = "" .
"UPDATE `".$this->table."` SET " .
"`".$this->fields["left"]."` = `".$this->fields["left"]."` - (".$idif."), " .
"`".$this->fields["right"]."` = `".$this->fields["right"]."` - (".$idif."), " .
"`".$this->fields["level"]."` = `".$this->fields["level"]."` - (".$ldif.") " .
"WHERE " .
"`".$this->fields["id"]."` IN (".implode(",", $node_ids).") ";
}
}
else {
$ewre = fSession::get('user[user_id]');
$sql[] = "" .
"INSERT INTO `".$this->table."` (" .
"`".$this->fields["owner"]."`, " .
"`".$this->fields["parent_id"]."`, " .
"`".$this->fields["position"]."`, " .
"`".$this->fields["left"]."`, " .
"`".$this->fields["right"]."`, " .
"`".$this->fields["level"]."` " .
") " .
"VALUES (" .
$ewre.", " .
$ref_id.", " .
$position.", " .
$idif.", " .
($idif + 1).", " .
$ldif.
")";
}
foreach($sql as $q) { $this->db->query($q); }
$ind = $this->db->insert_id();
if($is_copy) $this->_fix_copy($ind, $position);
return $node === false || $is_copy ? $ind : true;
}
}
Any help really appreciated.
Thanks
For anyone else this might help, here is my code after the changes to I made to make creating / moving nodes only apply to the particular tree in question.
Adding the owner_id in the where clause did in fact fix the issue. I needed to add it to only certain queries:
function _move($id, $ref_id, $position = 0, $is_copy = false) {
$hbhbhbh = fSession::get('nodes_allowed[nodes_access]');
if ($hbhbhbh == "0" || $hbhbhbh == "2" || $hbhbhbh == "3") {
if((int)$ref_id === 0 || (int)$id === 1) { return false; }
$sql = array(); // Queries executed at the end
$node = $this->_get_node_ifuueuwyhddd($id); // Node data
$nchildren = $this->_get_children($id); // Node children
$ref_node = $this->_get_node_ifuueuwyhddd($ref_id); // Ref node data
$rchildren = $this->_get_children($ref_id);// Ref node children
$ndif = 2;
$node_ids = array(-1);
if($node !== false) {
$node_ids = array_keys($this->_get_children($id, true));
// TODO: should be !$is_copy && , but if copied to self - screws some right indexes
if(in_array($ref_id, $node_ids)) return false;
$ndif = $node[$this->fields["right"]] - $node[$this->fields["left"]] + 1;
}
if($position >= count($rchildren)) {
$position = count($rchildren);
}
// Not creating or copying - old parent is cleaned
if($node !== false && $is_copy == false) {
$sql[] = "" .
"UPDATE `".$this->table."` " .
"SET `".$this->fields["position"]."` = `".$this->fields["position"]."` - 1 " .
"WHERE " .
"`".$this->fields["owner"]."` = ".(int) $node[$this->fields["owner"]]." AND " .
"`".$this->fields["parent_id"]."` = ".$node[$this->fields["parent_id"]]." AND " .
"`".$this->fields["position"]."` > ".$node[$this->fields["position"]];
$sql[] = "" .
"UPDATE `".$this->table."` " .
"SET `".$this->fields["left"]."` = `".$this->fields["left"]."` - ".$ndif." " .
"WHERE `".$this->fields["left"]."` > ".$node[$this->fields["right"]]." AND " .
"`".$this->fields["owner"]."` = ".(int) $node[$this->fields["owner"]];
$sql[] = "" .
"UPDATE `".$this->table."` " .
"SET `".$this->fields["right"]."` = `".$this->fields["right"]."` - ".$ndif." " .
"WHERE " .
"`".$this->fields["owner"]."` = ".(int) $node[$this->fields["owner"]]." AND " .
"`".$this->fields["right"]."` > ".$node[$this->fields["left"]]." AND " .
"`".$this->fields["id"]."` NOT IN (".implode(",", $node_ids).") ";
}
$sql[] = "" .
"UPDATE `".$this->table."` " .
"SET `".$this->fields["position"]."` = `".$this->fields["position"]."` + 1 " .
"WHERE " .
"`".$this->fields["parent_id"]."` = ".$ref_id." AND " .
"`".$this->fields["position"]."` >= ".$position." " .
( $is_copy ? "" : " AND `".$this->fields["id"]."` NOT IN (".implode(",", $node_ids).") ");
$ref_ind = $ref_id === 0 ? (int)$rchildren[count($rchildren) - 1][$this->fields["right"]] + 1 : (int)$ref_node[$this->fields["right"]];
$ref_ind = max($ref_ind, 1);
$self = ($node !== false && !$is_copy && (int)$node[$this->fields["parent_id"]] == $ref_id && $position > $node[$this->fields["position"]]) ? 1 : 0;
foreach($rchildren as $k => $v) {
if($v[$this->fields["position"]] - $self == $position) {
$ref_ind = (int)$v[$this->fields["left"]];
break;
}
}
if($node !== false && !$is_copy && $node[$this->fields["left"]] < $ref_ind) {
$ref_ind -= $ndif;
}
$sql[] = "" .
"UPDATE `".$this->table."` " .
"SET `".$this->fields["left"]."` = `".$this->fields["left"]."` + ".$ndif." " .
"WHERE " .
"`".$this->fields["owner"]."` = ".(int) $ref_node[$this->fields["owner"]]." AND `".$this->fields["left"]."` >= ".$ref_ind." " .
( $is_copy ? "" : " AND `".$this->fields["id"]."` NOT IN (".implode(",", $node_ids).") ");
$sql[] = "" .
"UPDATE `".$this->table."` " .
"SET `".$this->fields["right"]."` = `".$this->fields["right"]."` + ".$ndif." " .
"WHERE " .
"`".$this->fields["owner"]."` = ".(int) $ref_node[$this->fields["owner"]]." AND `".$this->fields["right"]."` >= ".$ref_ind." " .
( $is_copy ? "" : " AND `".$this->fields["id"]."` NOT IN (".implode(",", $node_ids).") ");
$ldif = $ref_id == 0 ? 0 : $ref_node[$this->fields["level"]] + 1;
$idif = $ref_ind;
if($node !== false) {
$ldif = $node[$this->fields["level"]] - ($ref_node[$this->fields["level"]] + 1);
$idif = $node[$this->fields["left"]] - $ref_ind;
if($is_copy) {
$sql[] = "" .
"INSERT INTO `".$this->table."` (" .
"`".$this->fields["parent_id"]."`, " .
"`".$this->fields["position"]."`, " .
"`".$this->fields["left"]."`, " .
"`".$this->fields["right"]."`, " .
"`".$this->fields["level"]."`" .
") " .
"SELECT " .
"".$ref_id.", " .
"`".$this->fields["position"]."`, " .
"`".$this->fields["left"]."` - (".($idif + ($node[$this->fields["left"]] >= $ref_ind ? $ndif : 0))."), " .
"`".$this->fields["right"]."` - (".($idif + ($node[$this->fields["left"]] >= $ref_ind ? $ndif : 0))."), " .
"`".$this->fields["level"]."` - (".$ldif.") " .
"FROM `".$this->table."` " .
"WHERE " .
"`".$this->fields["id"]."` IN (".implode(",", $node_ids).") " .
"ORDER BY `".$this->fields["level"]."` ASC";
}
else {
$sql[] = "" .
"UPDATE `".$this->table."` SET " .
"`".$this->fields["parent_id"]."` = ".$ref_id.", " .
"`".$this->fields["position"]."` = ".$position." " .
"WHERE " .
"`".$this->fields["id"]."` = ".$id;
$sql[] = "" .
"UPDATE `".$this->table."` SET " .
"`".$this->fields["left"]."` = `".$this->fields["left"]."` - (".$idif."), " .
"`".$this->fields["right"]."` = `".$this->fields["right"]."` - (".$idif."), " .
"`".$this->fields["level"]."` = `".$this->fields["level"]."` - (".$ldif.") " .
"WHERE " .
"`".$this->fields["id"]."` IN (".implode(",", $node_ids).") ";
}
} else {
$ewre = fSession::get('user[user_id]');
$sql[] = "" .
"INSERT INTO `".$this->table."` (" .
"`".$this->fields["owner"]."`, " .
"`".$this->fields["parent_id"]."`, " .
"`".$this->fields["position"]."`, " .
"`".$this->fields["left"]."`, " .
"`".$this->fields["right"]."`, " .
"`".$this->fields["level"]."` " .
") " .
"VALUES (" .
$ewre.", " .
$ref_id.", " .
$position.", " .
$idif.", " .
($idif + 1).", " .
$ldif.
")";
}
foreach($sql as $q) { $this->db->query($q); }
$ind = $this->db->insert_id();
if($is_copy) $this->_fix_copy($ind, $position);
return $node === false || $is_copy ? $ind : true;
}
}
Hope it helps someone...