Checking how many fields have changed upon saving a form - symfony1

I am saving records in a transaction using symfony1.4 and Doctrine.
The rows inserted are coming from a CSV file which is updated regularly. I have already got a method that checks if the records in the CSV match that in the DB and do not insert.
What I'm ideally wanting to do though, is to set a user flash telling them how many rows have been updated whenever they import the CSV file.
$conn = ProductTable::getInstance()->getConnection();
$conn->beginTransaction();
try {
$row = 1;
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
if ($row > 1) {
$values = array(
'blah' => null
);
$obj= ProductTable::getInstance()->findOrCreateNewProduct(
$values['blah']
);
$obj->merge($values);
$obj->save($conn);
}
$row++;
}
$conn->commit();
} catch (Doctrine_Exception $e) {
$conn->rollback();
throw $e;
}
I'm wondering how I'd get these updated fields. Is it in the actions.class.php or is it in the actual form.class.php file?
Thanks

On the you can call a Doctrine_Record::getModified() which will give you an array of fields modified (with their values though that doesnt matter for you). Then you can call count on the returned array and keep a cumulative total outside your loop.
$conn = ProductTable::getInstance()->getConnection();
$conn->beginTransaction();
$nbModified = 0;
try {
$row = 1;
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
if ($row > 1) {
$values = array(
'blah' => null
);
$obj= ProductTable::getInstance()->findOrCreateNewProduct(
$values['blah']
);
$obj->merge($values);
$nbModified += count($obj->getModified());
$obj->save($conn);
}
$row++;
}
$conn->commit();
// return $nbModified or otherwise do something with it here
} catch (Doctrine_Exception $e) {
$conn->rollback();
// youre rolling back so just for consistency set $nbModified to zero
$nbModified = 0;
throw $e;
}

Related

How to ignore first row (header row) on insert using PhpOffice\PhpSpreadsheet

I am looking for a way to ignore first row which is the header row when inserting data into mysql database using PhpOffice\PhpSpreadsheet.I have followed this but not workingSkip First Row in PHPSpreadsheet ImportMy problem is how to ignore the header row?
Below is my clean code...
use Phppot\DataSource;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Csv;
require_once ('./vendor/autoload.php');
if (isset($_POST["import"])) {
$allowedFileType = [
'application/vnd.ms-excel',
'text/xls',
'text/xlsx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
];
if (in_array($_FILES["file"]["type"], $allowedFileType)) {
$date = date('Y-m-d');
$targetPath = 'uploads/'.$date." ".$_FILES['file']['name'];
move_uploaded_file($_FILES['file']['tmp_name'], $targetPath);
$Reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
if($Reader) {
$Reader->setReadDataOnly(true);
$spreadSheet = $Reader->load($targetPath);
$sheetData = $spreadSheet->getActiveSheet()->toArray();
// Loop through the rows from xlsx file for insert
foreach($sheetData as $row) {
// get columns in a contigeous order from xlsx file
$regno = isset($row[0]) ? $row[0] : "";
$fullname = isset($row[1]) ? $row[1] : "";
$course = isset($row[2]) ? $row[2] : "";
$status = 1;
$pin = strtoupper(substr(md5(mt_rand()), 0, 10));
// Insert into db
$fieUploaded = $conn->itStudentsFileUpload($regno,$fullname,$course,$pin,$current_session,$status);
// If all is well send success message
if ($fieUploaded) {
echo "Success";
}
}
}
} else {
$type = "error";
$message = "Invalid File Type. Upload Excel File.";
}
}
After several tests, I solve the problem as follows:
class FirstRowFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter
{
public function readCell($column, $row, $worksheetName = '') {
// Return true for rows after first row
return $row > 1;
}
}
$Reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader("Xlsx");
$filterRow = new FirstRowFilter();
$Reader->setReadFilter($filterRow);
$spreadSheet = $Reader->load($targetPath);
$sheetData = $spreadSheet->getActiveSheet()->toArray();
Note:
If you have additional fields that are not from the excel file eg. date, you need to check for emptiness of the first row from the excel file and then insert if not empty to ignore inserting empty values.

Looping through custom fields to populate dynamic fields in Zapier

I'm not sure how to loop through the custom fields when adding a dynamic field via the web script editor.
When I test I can see the fields are being returned in the console
Where the number of fields is different with each instance of our app.
This is the code I'm using to return the data.
return z.request(options)
.then((response) => {
response.throwForStatus();
const results = z.JSON.parse(response.content)._embedded;
return results;
});
I assume I need to loop through each of the fields, pull out the ID and name and then put them back as an array of objects?
Something like this, only problem is nothing is being returned?
return z.request(options)
.then((response) => {
response.throwForStatus();
const results = z.JSON.parse(response.content).results._embedded;
var cFields = [];
for (var i = 0; i < results.length; i++) {
cFields.push({'id': results.customFields[i].label});
}
return cFields;
});
Any pointers?
I worked this out in the end. I think the problem was more because of my lack of coding knowledge. Not sure if this is the best answer but it worked.
return z.request(options)
.then((response) => {
response.throwForStatus();
const results = z.JSON.parse(response.content)._embedded;
let customFields = [];
for (let i = 0; i < results.customFields.length; i++) {
let customFieldsObj = {};
customFieldsObj['key'] = results.customFields[i].id;
customFieldsObj['label'] = results.customFields[i].label;
let helpText = results.customFields[i].type + ' Field';
customFieldsObj['helpText'] = helpText.toUpperCase();
customFields.push(customFieldsObj);
}
return customFields;
});

Display result matching optgroup using select2

I'm using select2 with Bootstrap 3.
Now I would like to know whether it is possible to display all optgroup items if the search matches the optgroup name while still being able to search for items as well. If this is possible, how can I do it?
The above answers don't seem to work out of the box with Select2 4.0 so if you're hunting for that, check this out: https://github.com/select2/select2/issues/3034
(Use the function like this: $("#example").select2({matcher: modelMatcher});)
function modelMatcher (params, data) {
data.parentText = data.parentText || "";
// Always return the object if there is nothing to compare
if ($.trim(params.term) === '') {
return data;
}
// Do a recursive check for options with children
if (data.children && data.children.length > 0) {
// Clone the data object if there are children
// This is required as we modify the object to remove any non-matches
var match = $.extend(true, {}, data);
// Check each child of the option
for (var c = data.children.length - 1; c >= 0; c--) {
var child = data.children[c];
child.parentText += data.parentText + " " + data.text;
var matches = modelMatcher(params, child);
// If there wasn't a match, remove the object in the array
if (matches == null) {
match.children.splice(c, 1);
}
}
// If any children matched, return the new object
if (match.children.length > 0) {
return match;
}
// If there were no matching children, check just the plain object
return modelMatcher(params, match);
}
// If the typed-in term matches the text of this term, or the text from any
// parent term, then it's a match.
var original = (data.parentText + ' ' + data.text).toUpperCase();
var term = params.term.toUpperCase();
// Check if the text contains the term
if (original.indexOf(term) > -1) {
return data;
}
// If it doesn't contain the term, don't return anything
return null;
}
Actually found the solution by modifying the matcher opt
$("#myselect").select2({
matcher: function(term, text, opt){
return text.toUpperCase().indexOf(term.toUpperCase())>=0 || opt.parent("optgroup").attr("label").toUpperCase().indexOf(term.toUpperCase())>=0
}
});
Under the premise that the label attribute has been set in each optgroup.
Found a solution from select2/issues/3034
Tested with select2 v.4
$("select").select2({
matcher(params, data) {
const originalMatcher = $.fn.select2.defaults.defaults.matcher;
const result = originalMatcher(params, data);
if (
result &&
data.children &&
result.children &&
data.children.length
) {
if (
data.children.length !== result.children.length &&
data.text.toLowerCase().includes(params.term.toLowerCase())
) {
result.children = data.children;
}
return result;
}
return null;
},
});
A few minor changes to people suggested code, less repetitive and copes when there are no parent optgroups:
$('select').select2({
matcher: function(term, text, opt){
var matcher = opt.parent('select').select2.defaults.matcher;
return matcher(term, text) || (opt.parent('optgroup').length && matcher(term, opt.parent('optgroup').attr("label")));
}
});

Async Futures running in sequence to completion

I encountered the following example (Example 1 below) of Futures which caused me to wonder if I could alter the way that I was handling Futures and remove all of the nested function calls that preserve order of processing, which however result in indentation which I find a bit messy.
The altered version of my program did not work however. It did not preserve the order of processing and did not “wait” for function to complete. For example, before returning from the first call (fGetUserInput), another subsequent function was called.
Why is it that in Example 1, all of the “1st level” “new Future”s processed sequentially, however in Example 2, my altered code, the order of processing is not preserved. While the call to fGetUserInput is being processed, one of the Futures that follows it is processed?
Is it perhaps that “Example 1” only “works” because all of the statements are synchronous?
I came across a reference to “runAsync”. Can that be used to achieve what I want? (process in sequence without all of the indentation).
// Example 1. Code that I encountered for Futures //
import 'dart:async';
main() {
new Future(() => print('1'))
.then((_) => print('a'))
.then((_) => print('b'));
new Future(() => print('2'))
.then((_) => print('c'))
.then((_) => print('d'));
new Future(() => print('3'))
.then((_) =>
new Future(() => print('e'))
.then((_) => print('f'))
);
new Future(() => print('4'))
.then((_) =>
new Future(() => print('g'))
.then((_) => print('d'))
);
}
The above results in the following console output order :-
1 a b 2 c d 3 4 e f g d
Which I thought made sense.
Therefore, I modified my code to test it as follows :-
// Example 2. Altered version of my code which //
// does not preserve the order of processing, //
// which is necessary for program to function. //
new async.Future(() => fGetUserInput())
.then((lInput) {
iMaxIters = int.parse(lInput[4]);
tClearTable = (lInput[5] == "y");
iDivisor = fInitialize(iMaxIters);
tgPrint = false; // printing off
sUri =
"postgres://${lInput[1]}:${lInput[2]}#localhost:5432/${lInput[3]}";
sStartTime = lInput[7];
})
.catchError((oError) => fFatal("Get User Input", oError));
new async.Future(() => fConnectToDb(sUri, sStartTime))
.then((bool tConnected) {
if (ogDb == null)
fFatal("Unable to connect to database", "");
print ("Processing database ......");
})
.catchError((oError) => fFatal("Connect to Db", oError));
new async.Future(() => fClearTable(tClearTable))
.then((sResult) => print (sResult+"\n"))
.catchError((oError) => fFatal("Clear Table", oError));
new async.Future(() => fProcessInserts(iMaxIters, iDivisor))
.then((sResult) => print (""))
.catchError((oError) => fFatal("Process Inserts", oError));
new async.Future(() => fSetupRandKeys())
.then((sResult) => print (""))
.catchError((oError) => fFatal("Setup Random Keys", oError));
new async.Future(() => fProcessUpdates(iMaxIters, iDivisor))
.then((sResult) {
String sTotValue = fFormatAmount(igGrandTotAmt, true, 2);
fPrint ("Grand Total added to database = \$${sTotValue}");
ogDb.close();
exit(0);
})
.catchError((oError) => fFatal("Process Updates", oError));
}
void fFatal (String sMessage, Error oError) {
print("\n\nFatal Error. $sMessage\n${oError}");
exit(1);
}
async.Future<String> fProcessInserts(int iMaxIters, int iDiv) {
async.Completer oCompleter = new async.Completer<String>();
int iTot = 0;
Function fLoop;
print ("\nProcessing Inserts ......");
fResetAndStartWatch();
The following is my code prior to the above changes, and the following Example 3 appears to work OK. I don't like the extent of indentation, and in situations with more function calls, that would increase the extent of indentation. I was hoping for a more elegant way to do it.
// Example 3: The original version of my code //
// which does preserve the order of processing //
void main() {
print("");
String sCheckPoint = "Get User Input";
fGetUserInput()
.then((lInput) {
int iMaxIters = int.parse(lInput[4]);
bool tClearTable = (lInput[5] == "y");
int iDiv = fInitialize(iMaxIters);
tgPrint = false; // printing off
String sUri =
"postgres://${lInput[1]}:${lInput[2]}#localhost:5432/${lInput[3]}";
sCheckPoint = "Connect to Database";
fConnectToDb(sUri, lInput[7]).then((bool tConnected) {
if (ogDb == null)
fFatal(sCheckPoint, "Unable to conenct to Db");
print ("Processing database ......");
sCheckPoint = "Clear Table";
fClearTable(tClearTable).then((sResult) {
print (sResult+"\n");
sCheckPoint = "Process Inserts";
fProcessInserts(iMaxIters, iDiv).then((sResult) {
print;
sCheckPoint = "Set-up Random Keys";
fSetupRandKeys().then((sResult) {
print;
sCheckPoint = "Process Updates";
fProcessUpdates(iMaxIters, iDiv).then((sResult) {
String sTotValue = fFormatAmount(igGrandTotAmt, true, 2);
fPrint ("Grand Total added to database = \$${sTotValue}");
ogDb.close();
exit(0);
});
});
});
});
});
})
.catchError((oError) => fFatal(sCheckPoint, oError));
}
void fFatal (String sMessage, Error oError) {
print("\n\nFatal Error. $sMessage\n${oError}");
exit(1);
}
async.Future<String> fProcessInserts(int iMaxIters, int iDiv) {
async.Completer oCompleter = new async.Completer<String>();
int iTot = 0;
Function fLoop;
print ("Processing Inserts ......");
fResetAndStartWatch();
Remember that you can chain futures, which will reduce your indentation by quite a bit.
The downside is that you don't get nested scopes, which can be useful if you have more than one value to propagate between async blocks, but that can be worked around in a few ways.
Here's you example 3 with chaining:
// Example 3 with chaining
void main() {
String checkPoint = "Get User Input";
getUserInput().then((input) {
int maxIters = int.parse(input[4]);
bool clearTable = (input[5] == "y");
int div = initialize(maxIters);
shouldPrint = false; // printing off
String uri =
"postgres://${input[1]}:${input[2]}#localhost:5432/${input[3]}";
checkPoint = "Connect to Database";
return connectToDb(uri, input[7]).then((bool connected) {
if (db == null)
fatal(checkPoint, "Unable to conenct to Db");
print ("Processing database ......");
checkPoint = "Clear Table";
return clearTable(shouldClearTable);
}).then((result) {
print (result+"\n");
checkPoint = "Process Inserts";
return processInserts(maxIters, div);
}).then((result) {
print('');
checkPoint = "Set-up Random Keys";
return setupRandKeys();
}).then((result) {
print('');
checkPoint = "Process Updates";
return processUpdates(maxIters, div);
}).then((result) {
String totValue = formatAmount(grandTotAmt, true, 2);
print("Grand Total added to database = \$${totValue}");
return db.close();
// exit(0); pretty much never call exit()
});
}).catchError((error) => fatal(checkPoint, error));
}
Edit: Oops, looking more closely I got bit by the scoping problem... I added a level of nesting just to capture the needed vars in a scope accessible by the following blocks. I'm also removing the hungarian-ish notation, because... don't do that in Dart :)

Entity Framework 5 - read a record then delete it in loop

I am having issues with my application. I have a db table for a print queue. When I read from that table in a loop, once I add that record to the view model, I then want to delete it from the database...this would be the most efficient way to do it, but EF barks:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
I've tried using multiple contexts... but that didn't seem to work either. I've seen articles like Rick Strahl's, but frankly it was above my level of understanding, and not exactly sure if it helps my issue here and seemed quite an in depth solution for something as simple as this.
Is there a simple way to accomplish what I am trying to achieve here?
Here is my code:
public List<InventoryContainerLabelViewModel> CreateLabelsViewModel(int intFacilityId)
{
var printqRep = new Repository<InventoryContainerPrintQueue>(new InventoryMgmtContext());
var printqRepDelete = new Repository<InventoryContainerPrintQueue>(new InventoryMgmtContext());
IQueryable<InventoryContainerPrintQueue> labels =
printqRep.SearchFor(x => x.FacilityId == intFacilityId);
List<InventoryContainerLabelViewModel> labelsViewModel = new List<InventoryContainerLabelViewModel>();
if (labels.Count() > 0)
{
//Get printq record
foreach (InventoryContainerPrintQueue label in labels)
{
IEnumerable<InventoryContainerDetail> icDtls =
label.InventoryContainerHeader.InventoryContainerDetails;
//Get print details
foreach (InventoryContainerDetail icDtl in icDtls)
{
labelsViewModel.Add(new InventoryContainerLabelViewModel()
{
...
populate view model here
}
);//Add label to view model
} //for each IC detail
//Delete the printq record
printqRepDelete.Delete(label); <======== Error Here
} //foreach label loop
}//label count > 0
return labelsViewModel.ToList();
}
In the end, I added a column to the printq table for status, then in the the loop updated it to processed, then called a separate method to delete it.
public List<InventoryContainerLabelViewModel> CreateLabelsViewModel(int intFacilityId)
{
InventoryMgmtContext dbContext = new InventoryMgmtContext();
var printqRep = new Repository<InventoryContainerPrintQueue>(dbContext);
IEnumerable<InventoryContainerPrintQueue> unprintedPrtqRecs =
printqRep.SearchFor(x => x.FacilityId == intFacilityId && x.Printed == false);
List<InventoryContainerLabelViewModel> labelsViewModel = new List<InventoryContainerLabelViewModel>();
if (unprintedPrtqRecs.Count() > 0)
{
//Get printq record
foreach (InventoryContainerPrintQueue unprintedPrtqRec in unprintedPrtqRecs)
{
IEnumerable<InventoryContainerDetail> icDtls =
unprintedPrtqRec.InventoryContainerHeader.InventoryContainerDetails;
//Get container details to print
foreach (InventoryContainerDetail icDtl in icDtls)
{
labelsViewModel.Add(new InventoryContainerLabelViewModel()
{
...
}
);//Get IC details and create view model
} //for each IC detail
unprintedPrtqRec.Printed = true;
printqRep.Update(unprintedPrtqRec, unprintedPrtqRec, false);
} //foreach label loop
//Commit updated to Printed status to db
dbContext.SaveChanges();
}//label count > 0
return labelsViewModel;
}
public ActionConfirmation<int> DeletePrintQRecs(int intFacilityId)
{
InventoryMgmtContext dbContext = new InventoryMgmtContext();
var printqRep = new Repository<InventoryContainerPrintQueue>(dbContext);
IEnumerable<InventoryContainerPrintQueue> printedPrtqRecs =
printqRep.SearchFor(x => x.FacilityId == intFacilityId && x.Printed == true);
foreach (InventoryContainerPrintQueue printedPrtqRec in printedPrtqRecs)
{
//Delete the printq record
printqRep.Delete(printedPrtqRec, false);
}
//Save Changes on all deletes
ActionConfirmation<int> result;
try
{
dbContext.SaveChanges();
result = ActionConfirmation<int>.CreateSuccessConfirmation(
"All Label Print Q records deleted successfully.",
1);
}
catch (Exception ex)
{
result = ActionConfirmation<int>.CreateFailureConfirmation(
string.Format("An error occured attempting to {0}. The error was: {2}.",
"delete Label Print Q records",
ex.Message),
1
);
}
return result;
}

Resources