Categorizing non-ID categories PHP loop - php

On my project there're various search results of different content-types. For unimportant reasons, not all content-types carry a unique ID. However, I tried to write this loop that will detect IDless content-types, and will give them a unique ID.
Basically, the results look like this:
Category ID 3
Category ID 3
Category ID 4
NON-ID Category 1
NON-ID Category 2
[...]
I tried this:
$current = $result->section;
// if there's a category ID -- use it
if ($current != null) echo $result->sectionid;
else
// if this is the first IDless category -- initialize. this happens once.
if ($virginity == true) {
$store = $current;
$virginity = false;
}
// if initialized and current category string is the same as stored category string -- print it
if (($virginity == 0) && ($store = $current)) {
echo $defaultID;
}
// if initialized and current category string is different from the stored category string -- set new default category +1 and print it
if (($virginity == false) && ($store != $current)) {
$defaultID = $defaultID++;
echo $defaultID;
$store = $current;
}
Can you see where I'm going here?
Trying to give each new category that comes up in the search results a unique ID, on the next loop session - check if the category string is same as the previous one, if so -- use the same ID, else -- bump ID by 1, print the new ID and store a new category.
However: This doesn't work properly.
Why?

Your code is a little tough to read, but a big red flag I see is this block:
//You have a single equals sign here v
if (($virginity == 0) && ($store = $current))
{
echo $defaultID;
}
That means that as long as $virginity==0, that line will always be run, and $store will always equal $current.
By the way, I'm going to recommend some reformatting and readability suggestions. Take them or leave them , though, it's just my opinion.
$current = $result->section;
if ($current != null)
{//if one of the results has brackets, put brackets on both
echo $result->sectionid;
}
else if ($virginity == true)
{// if this is the first IDless category -- initialize. this happens once.
$store = $current;
$virginity = false;
}
//you're setting $virginity=false or true, so doing !$virginity makes sense
// if it was actually a number, then comparing to 0 is better
// also, use parentheses only where really needed, otherwise it's just clutter
if (!$virginity && $store == $current)
{
echo $defaultID;
}
if (!$virginity && $store != $current)
{// if initialized and current category string is different from the stored category string -- set new default category +1 and print it
$defaultID = $defaultID++;
echo $defaultID;
$store = $current;
}

Related

PHP recursive creation of db queries after multidimensional form submission: fk field sometimes "omitted", but why and how to fix?

NOTE: SOLUTION FOUND
I am trying to build a (my first!) PHP/JQuery/MySQL web app able to work with multidimensional data. In current state, almost everything works fine but one strange bug occurs when submitting data (see title) and I haven't found any explanation. Can anyone open my eyes?
When working my test form with 5 dimensional data (table names - one, two, three, four, five - all joined in chain):
if I submit completely new entry with all dimensions then all 5 INSERT INTO queries are generated correctly
but if I add to the existing entry (under 2nd dim) new 3rd dimension with corresponding child data (4th and 5th) - that means tables three, four and five - then foreign key field in table five (four_id) is omitted from the insertion query
all the rest options (two, three, four and five or new four and five) have no issues
There are 3 functions in php that are doing the work (first for main table, second for next 2 dimensions and third (recursive) for next n dimensions). As tables four and five in this example are "belonging" to the recursive one I am quite sure that the issue and key for solution should be there.
Each function is using both form data and existing data that is already submitted. Pk value of parent row is passed to the child in two possible ways:
After each INSERT INTO query a MySQL variable for new pk value is created: SET #last_id_tablename = LAST_INSERT_ID() to be used in child query when needed. If data submitted to parent and child (say four and five) is new for both tables then child table's query should be (and normally is)
INSERT INTO five (four_id, title) VALUES (#last_id_four, 'Some text')
If parent data is already existing and we add new related child row then the existing parent pk value (say 1) is passed to the child and query is
INSERT INTO five (four_id, title) VALUES (1, 'Some text')
So the issue is that when I have an entry with first 2 dimensions and I add 3 dimensions under existing 2nd (IOW I have parent row in one with its child row in two and under this I add new data starting from table three the generated queries are:
INSERT INTO three (two_id, title) values (1, 'Some text');
INSERT INTO four (three_id, title) values (#last_id_three, 'Some text')
INSERT INTO five (title) values ('Some text')
As you see, four_id and #last_id_four are missing in third line.
All other combinations including fully new data submmission for all dimensions are generating a correct query for five. Fully new data submission query list looks like this one (first table's last id is returned before the rest continues, passed to the next function and therefore it's in use already as a real number, let's say 10)
INSERT INTO one (title) values ('Some text');
INSERT INTO two (one_id, title) values (10, 'Some text');
SET #last_id_two = LAST_INSERT_ID();
INSERT INTO three (two_id, title) values (#last_id_two, 'Some text');
SET #last_id_three = LAST_INSERT_ID();
INSERT INTO four (three_id, title) values (#last_id_three, 'Some text')
SET #last_id_four = LAST_INSERT_ID();
INSERT INTO five (four_id, title) values (#last_id_four, 'Some text')
The only one explanation I thought about was that it's somehow related to the variable names in the recursive function and therefore I renamed all of vars but it didn't resolve the issue.
Below I show the full code of this recursive function
/*
Recursive function for inserting or editing nested data (since 4th until nth level)
$subTable - current table where we insert new or edit existing data
$subData - current table's data in form view
$existingSubJoin - current table data that is already in database (submitted earlier)
$existing... - corresponding variables for existing data
$parentTable - current table's parent table (where current tables FK is pointing)
$existingParentJoin - parent table data that already exists
$parentPkField, $parentPkValue - the names are self-explanatory
$parentPkValue can be a real number from existing row or #last_id_$parentTable
#last_id_$subTable - a MySQL variable that passes the last_insert_id() value from newly submitted parent row to the child row's FK
$subSingle - a new array of db field values for one row
$subSet - array for UPDATE statements (SET field = 'value', field2 = 'value2' etc)
$subFields - array of fields for INSERT INTO
$nextLastId = pk value or #last_id_$subTable to be passed as a last argument for next recursion
*/
public function buildQueryListChild($subTable, $subData, $existingSubJoin, $parentTable, $existingParentJoin, $parentPkField, $parentPkValue)
{
if (isset($subData))
{
foreach($subData as $sKey => $subRow)
{
$subSingle = array();
if (!isset($existingSubJoin['rows'][$sKey]))
{
$existingSubRow = $existingSubJoin['rows'][0];
}
else
{
$existingSubRow = $existingSubJoin['rows'][$sKey];
}
$subSet = array();
$subParentId = $parentTable . '_' . $parentPkField;
foreach ($subRow as $subField => $subValue)
{
if (isset($existingSubJoin['properties']['fields']))
{
foreach ($existingSubJoin['properties']['fields'] as $existingSubField)
{
if ($existingSubField['name'] == $subField)
{
if ($existingSubField['key'] == 'PRI')
{
$subRowPkField = $existingSubField['name'];
$subRowPkAlias = $existingSubField['alias'];
}
else
{
$subRowField = $existingSubField['name'];
$subRowAlias = $existingSubField['alias'];
$subRowType = $existingSubField['type'];
}
$sNumTypes = array('int', 'float', 'decimal', 'numeric', 'double', 'bit');
foreach ($sNumTypes as $sType)
{
$sNumber = strpos($existingSubField['type'], $sType) === true ? true : null;
}
$sString = $sNumber ? false : true;
}
}
}
if (empty($subRow[$subRowPkField]))
{
$newSub = true;
$updateSub = false;
}
else
{
$updateSub = true;
$newSub = false;
}
if (!is_array($subValue))
{
if ($subField != $subRowPkField && strpos($subRowType, 'timestamp') === false)
{
if ($subField == $subParentId)
{
$subSingle[$subParentId] = $parentPkValue;
}
else
{
if (!empty($subValue)) $subSingle[$subField] = $subValue;
}
if ($updateSub && $subField == $subRowField && $subSingle[$subField] != $existingSubRow['data'][$subRowAlias])
{
$uSubField = $subField;
$uSubValue = $subValue;
if (!$sNumber)
{
$uSubValue = "'$subValue'";
}
$subSet[$uSubField] = "$uSubField = $uSubValue";
}
}
}
}
if (!empty($subSet))
{
$subSets = implode(', ', $subSet);
$subRowPkValue = $subRow[$subRowPkField];
$current = "UPDATE $subTable SET $subSets WHERE $subRowPkField = $subRowPkValue;\n";
$sql .= $current;
}
if ($newSub)
{
$subRowPkValue = $subRow[$subRowPkField];
if (!empty($subSingle))
{
$subFields = implode(', ', array_keys($subSingle));
$subValues = "'" . implode("', '", array_values($subSingle)) . "'";
$subValues = str_replace("'$parentPkValue'", "$parentPkValue", $subValues);
$current = "INSERT INTO $subTable ($subFields) VALUES ($subValues);\n";
$sql .= $current;
$sql .= "SET #last_id_$subTable = LAST_INSERT_ID();\n";
}
}
foreach ($subRow as $sTable => $sData)
{
if (is_array($sData))
{
if (isset($existingSubJoin['rows'][$sKey]) && $sKey > 0)
{
$nextLastId = $sKey;
}
else
{
$nextLastId = "#last_id_$subTable";
}
$existingSData = $existingSubRow['joins']->$sTable;
$sql .= $this->buildQueryListChild($sTable, $sData, $existingSData, $subTable, $existingSubJoin, $subRowPkField, $nextLastId);
}
}
}
}
return $sql;
}
You see there a line
$current = "INSERT INTO $subTable ($subFields) VALUES ($subValues);\n";
where both $subFields and $subValues are imploded from corresponding submission array (array_keys and array_values) that is created in
if ($subField == $subParentId)
{
$subSingle[$subParentId] = $parentPkValue;
}
else
{
if (!empty($subValue)) $subSingle[$subField] = $subValue;
}
And as said, ($subFields) should always contain parenttable_id and ($subValues) its existing value or #last_id_parenttable
Sorry for this amount of information and thanks in advance for help!
SOLUTION FOUND - see ANSWER
There were also other issues that occurred in my code but, like changes I made in parent function, this is outside of this issue's scope. I hope all my explanations are clear :)
The (main?) cause was that the parent pk field name was not always passed to child. I discovered this when I got idea that the way how the fk value was set was not the best one and that this should be done at very beginning, before creating a new array. So the first thing I did was that I moved it right before iterating the fields
$subParentId = $parentTable . '_' . $parentPkField;
$subRow[$subParentId] = $parentPkValue;
foreach ($subRow as $subField => $subValue)
{ ... }
But it wasn't enough. Yes, I got the needed value but without $parentPkField so I got a nonexisting field name (parenttablename_). Therefore it was clear what I should really look for.
And I got it. Some time ago I built among others a "blank row" feature to be used in some cases when there is no real db row. In this case I just forgot to use it where needed :D
Therefore I had to make some corrections also to the parent function and pass an additional $blank array from there to the child. And of course corresponding changes to the current function. This way the existence of $parentPkField was ensured.
The diffs in this function are here (old commented, new below or otherwise explained)
/*
public function buildQueryListChild($subTable, $subData, $existingSubJoin, $parentTable, $existingParentJoin, $parentPkField, $parentPkValue)
*/
public function buildQueryListChild($subTable, $subData, $existingSubJoin, $blank, $parentTable, $existingParentJoin, $parentPkField, $parentPkValue)
{
.....
if (!isset($existingSubJoin['rows'][$sKey]))
{
// $existingSubRow = $existingSubJoin['rows'][0];
$existingSubRow = $blank['rows'][0];
}
......
// Added
$subRow[$subParentId] = $parentPkValue;
......
foreach ($subRow as $subField => $subValue)
{
// Added
if (!$existingSubJoin) $existingSubJoin = $blank;
......
if ($subField != $subRowPkField && strpos($subRowType, 'timestamp') === false)
{
/*
if ($subField == $subParentId)
{
$subSingle[$subParentId] = $parentPkValue;
}
else
{
if (!empty($subValue)) $subSingle[$subField] = $subValue;
}
*/
if (!empty($subValue)) $subSingle[$subField] = $subValue;
.......
if ($newSub)
{
// Removed as unneeded
//$subRowPkValue = $subRow[$subRowPkField];
if (!empty($subSingle))
{
........
// if / else moved here, see below foreach
if (isset($existingSubJoin['rows'][$sKey]) && $sKey > 0)
{
$nextLastId = $sKey;
}
else
{
$nextLastId = "#last_id_$subTable";
}
foreach ($subRow as $sTable => $sData)
{
if (is_array($sData))
{
/*
if (isset($existingSubJoin['rows'][$sKey]) && $sKey > 0)
{
$nextLastId = $sKey;
}
else
{
$nextLastId = "#last_id_$subTable";
}
*/
// The commented lines above: moved them before foreach but actually not sure if it had any impact
//Added
$nextBlank = $blank['rows'][0]['joins']->$sTable;
/*
$sql .= $this->buildQueryListChild($sTable, $sData, $existingSData, $subTable, $existingSubJoin, $subRowPkField, $nextLastId);
*/
$sql .= $this->buildQueryListChild($sTable, $sData, $existingNextData, $nextBlank, $subTable, $existingSubJoin, $subRowPkField, $nextLastId);

Something that should be added to a list in an if statment is not being added

I am looping through a list of objects: however a couple of times (and always for the same ones) it will not work as expected.
I am trying to add the teacher object to the list if its ID does not match he Teacher ID of a previous Lesson in that time period (SubBlock/SB). The one particular teacher that it doesn't seem to like is a teacher who is free to teach all the lessons, out of the 6.
I am trying to get it to pick that teacher and it works on attempt 1,3,5 but not 2,4,6.
Here is the relevant function:
function getRelaventFreeTeachers(&$PossibleTeachers, $Teachers, $Lessons, $SB){
foreach ($Teachers as $TeacherID){
$Usability = 1;
foreach ($Lessons as $Lesson){
$LT = $Lesson->getLessonTeacher();
$T = $TeacherID->getTID();
$LSB = $Lesson->getBlock();
if ($LT != $T){
if ($LSB != $SB){
$Usability = 1;
}
}else{
$Usability = 0;
}
}
if ($Usability == 1){
array_push($PossibleTeachers, $TeacherID);
}
}
}
The question is not really very clear on the condition to add or not add a possible teacher, but some things seems wrong in the code:
Once the variable $Usability is set to 0, your code does not prevent it from getting 1 again in a subsequent iteration of the inner loop. That does not look right. You should exit the inner loop as soon as you detect it is not OK.
The case where you set $Usability to 1 is therefore not necessary: it already is 1 from the start, and when it gets 0 you exit. You wrote of a condition where the teacher should only be checked for a particular period. If I interpret that correctly then the code might need to look like this:
if ($LT == $T && $LSB == $SB) { // Is the second condition indeed what you want?
$Usability = 0;
break;
}

adding up sums of multiple variables in php

ok
so I have been stuck on this problem for a while and I'm sure there is a more elegant way of doing it.
I have 3 columns in a database -
Stand1 | Stand2 | Stand3
each column will either have a stand number (i.e D30) or 'Not Assigned'
What I need to do is count how many stands have been assigned to that customer.
For example :
D30 | not assigned | not assigned would = 1
D30 | B30 | E30 = 3
The code I have so far is
if ($row["stand1"] == 'Not Assigned') {
$stand1 = '0';
}
else {
$stand1 = '1';
}
if ($row["stand2"] == 'Not Assigned') {
$stand2 = '0';
}
else {
$stand2 = '1';
}
if ($row["stand3"] == 'Not Assigned') {
$stand3 = '0';
}
else {
$stand3 = '1';
}
I then thought I could try and count how many stand were assigned :
$standcount = $stand1 + $stand2 + $stand3
But when I print this out (everything else is in an array so loops through every customer) all I get is the total 3 for every customer.
I tried saving the data into an array like:
$standcount = array($stand1, $stand2, $stand3);
But I get the same result. I thought a foreach loop may work but not sure how to do it.
I would suggest you to:
Set the columns' default value to NULL, so that if no value is provided in an entry you'll have a null value instead of a string (that can contains different letters, as in your case). After that you can simply perform the isset() check against each value.
Use a function to avoid code repetition (the main source of errors), something like:
function check($value) {
return (isset($value)) ? 1 : 0;
}
$stand_1 = check($row["stand1"]);
$stand_2 = check($row["stand2"]);
$stand_3 = check($row["stand3"]);
If you instead want to use a string as "Not Assigned" or "NA", you can perform a case-insensitive check with strcasecmp() between the strings in case, for any reason, a value contains an extra space or a lower-case letter.
In this case the check function would be:
function check($str) {
return (strcasecmp($str, "NA") == 0) ? 0 : 1;
}
$stand_1 = check($row["stand1"]);
$stand_2 = check($row["stand2"]);
$stand_3 = check($row["stand3"]);

Calculate no of fields in MYSQL between two data types

OK weird question.
Table consists of multiple fields. Some with data type int(5) and the rest with datatype int(11)
So lets take a row...
id =>int(5)
var1 =>int(11)
var2 =>int(11)
var3 =>int(11)
var4 =>int(11)
var5 =>int(5)
var6 =>int(11)
var7 =>int(11)
var8 =>int(11)
How can I count the fields in PHP BETWEEN id (int(5)) and var (int(5))
I need to return the values in the fields between using php... but Im stuck. Bad table design doesnt help...but Im stuck with it.
The full scenario is that i need to create an if statement which says, output the name and and data of the int(5) field IF any of the fields between it and the next int(5) contain data
sorry... ugly I know!!!
If run this so far
$sql2 = 'SHOW COLUMNS from services2';
$result2 = getDBResults($sql2, $level, $testing);
$total = count($result2);
$i=2;
while ($i<($total-1))
{
$data = $result2[$i]['Field'];
if ($result2[$i][1]=='int(5)')
{
$html .= '<h2>'.preg_replace('/(?<!\ )[A-Z]/', ' $0', $result2[$i]['Field']).'</h2>';
}
else
{
];
// "$result - This is a seperate query run previously to get the row
if ($result[0][$data] == 1)
{
$html .= '<p>'.preg_replace('/(?<!\ )[A-Z]/', ' $0', $data).'</p>';
}
}
$i++;
}
I have not had a chance to actually test this, so dont beat me up if you need to tweek it a bit to smooth off any rough edges.
But its a fairly standard, if not totally simple, process of processing over the column names result array in a loop and just remembering the last int(5) column name in a variable so you can use it, if and only if, you have found some data in any int(11) column types before seeing the next int(5) column type.
Now I understand why you had trouble explaining what you wanted, I am having the same trouble describing a solution.
Anyway, have a look at this code and see if it makes any sence to you.
$sql2 = 'SHOW COLUMNS from services2';
$columns = getDBResults($sql2, $level, $testing);
$total = count($columns);
$print_column = null;
$found_data = false;
foreach ( $columns as $idx => $column ) {
// skip first 2 columns
if ($idx < 3 ) { continue; }
// first time thru loop so remember the first int5 column name we saw
if ( $column['Type'] == 'int(5)' && empty($print_column) ) {
$print_column = $column['Field'];
$found_data = false;
continue;
}
// found the next int5 column, do we need to print anything
if ( $column['Type'] == 'int(5)' && $found_data ) {
// This is where you would do any output creation,
// I just kept it simple.
echo $result[0][$print_column];
// reset state and the next field that might be printed.
$found_data = false;
$print_column = $column['Field'];
continue;
}
// This may not need to be an if, but just in case you have other field types
// that you are not interested in processing I made it an if.
if ( $column['Type'] == 'int(11)' && ! empty($result[0][$column['Field']]) ) {
$found_data = true;
}
} // endforeach

How to filter foreach array for integers

I have a for-each statement based off a php generated query..
I am trying to figure out how I can make sure all the IDDestinations of the records are the same.
For example in my current query I have 2 records with an IDDestination of 12, one record with an IDDestination of 9 and the last is 3.
I know how to do this in the query but I am trying to generate a message to the user if the IDDestinations are not equivalent.
My code so far.
foreach($results as $row) {
$IDDestination =(int) $row['IDDestination'];
if ($IDDestination == $IDDestination){
echo "<script>alert('All Courses Match Destination.');</script>";
} else {
echo "<script>alert('Courses have different Destinations);</script>";
}
var_dump($IDDestination);
}
This is currently just verifying that each record has an IDDestination Present and tells ME All courses Match.
How can I make it so the INTEGERS are equivalent and give me the same message?
Here's one way; use a variable outside your loop to determine if it's ok or not:
$everything_matches = true;
$id = null;
foreach($results as $row) {
// Set it for the first record.
if($id === null)
$id = $row['IDDestination'];
// If the current iteration's ID matches the first record, $everything_matches
// will stay true, otherwise it's false and we should kill the loop.
if($id != $row['IDDestination']) {
$everything_matches = false;
break;
}
}
// Output your message
$message = $everything_matches ? 'All courses match destination.' : 'Courses have different destinations.';
echo '<script>alert("' . $message . '");</script>';

Categories