Laravel excel validate two columns' combination is not duplicate - php

I have a requirement wherein I need to ensure that Excel file being uploaded by user does not have duplicate rows w.r.t. 2 particular columns.
Example:
In the snippet below, I want to flag out that row 1 and 2 contain duplicate combination of COMPANY_CODE and CLERK_CODE:
If such duplicate combination is found I want to reject the entire file being imported and let the user know where the problem is.
Any clues?

Not sure if Maat/Laravel Excel can solve this easily. So, I went ahead and created associative array with key as concatenation of two columns which I don't want to repeat in Excel.
Then I check manually using a foreach loop that if key exists in associative array, it means there is duplicate entry in Excel.
Some Sample code for reference below:
$array = Excel::toArray(new MyExcelImport(), request()->file);
$assoc_array = array();
foreach ($array[0] as $key => $value) {
$new_key = $value['company_code'] . $value['clerk_code'];
// Presence of combination of company_code and clerk_code in the assoc_array indicates that
// there is duplicate entry in the Excel being imported. So, abort the process and report this to user.
if (array_key_exists($new_key, $assoc_array)) {
return response()->json("Combination of company_code: " .
$value['company_code'] .
" and clerk_code: " .
$value['clerk_code'] .
" is duplicate in the file being imported. Please correct same and retry upload.", 422);
}
$assoc_array[$new_key] = $value;
}
Hope this helps someone with similar needs!

Related

How grouping rows by field in PHPExcel

I have a problem when creating excel files with the phpExcel library.
I want to create groups on certain lines based on the sales column (with same sales name).
I have made the file manually by using the Subtotal feature on the Data tab in Excel.
Is it possible that phpexcel has such feature?
You can see the sample file that I mean on the link / image that I uploaded.
You can group the rows like this
$objPHPExcel->getActiveSheet()->getRowDimension('5')->setOutlineLevel(1);
For more details you can visit here.
You can also go here and here for working examples
Using the PHPExcel , you can use the methods :
setOutlineLevel($level) // the level of the current row
setVisible(false) // show or hide the group
setCollapsed(true) // add collapse to group (as figure out in the picture above )
THe point here is to define group condition , example :
We suppose that the file excel is already generated with PHPExcel and
now you are going to read the file again for grouping rows .
First let's read the column C that contain name of salesman and store them inside the $salemans :
$salesmans = array();
/*
the output of $salesmans seems to be something like :
[0] ==> "Abdul Karim",
[1] ==> "Apan Total",
[2] ==> ""Ari Total
*/
The array $salesman should have distinct value , so when trying to
insert inside the array verify if the current value doesn't exist
already in the $salesmans.
Example :
$salesmans=array();
if (!in_array($currentSalesman, $salesmans))
{
$array[] = $value;
}
Here , we are going to to set the level of each row by getting the value of the current salesman with the key in the $salesmans
Caution : please try to modify this section because i'm not getting
how you manage fetch rows.I'm just making code more clear to understand easy
for ($row = 0; $row <= 10000; ++$row) {
$currentsalesman = $row['c']; //
$keylevel = array_search($currentsalesman, $salesmans);// this will return the key in $salesmans array
$objPHPExcel->getActiveSheet()
->getRowDimension($row)
->setOutlineLevel($keylevel) // set here the level .
->setVisible(false)
->setCollapsed(true);
}

PHP incrementing all IDs in a CSV

thanks for reading!
I have an app that allows people to add, edit and delete items in a CSV. I've encountered a bug where if there are non-unique IDs and you try to edit or delete them, it will edit or delete all of them, as the system parses through the spreadsheet to find the ID - which also corresponds to the object's order when using it so the user must be able to change the ID
The solution I've come up with is quite simple, should the user edit an object and change its ID to one that already exists, then the system will take all of the objects with an ID bigger than or equal to the new ID and increment them all by one.
The following code is my if statement that checks whether the ID already exists
if($exists == "true") //does the $newImageID already exist in the gallery?
{
$table = fopen($fullURL,'r'); //$fullURL is the location of the CSV tested and works
$temp_table_two = fopen($tempURL,'w');
while (!feof($temp_table_two) ) {
$getid = fgetcsv($temp_table_two, 1024);
if($getid[0] >= $newImageID)
{
// $getid[0]++; //increase id in temp_table_two by 1 if it is > $newImageID
echo $getid[0];
}
}
fclose($table);
fclose($temp_table);
rename($tempURL,$fullURL);
}
This code takes place after fopen and before fclose. In context, $exists is either "true" or "false" (will change to boolean later on), the while loop parses through my $temp_table (a fopen) and if the first column object (the ID) is equal to or bigger than the one in the new ID then it is incremented. This means that the new object gets "slotted in" so to speak and pushes the rest down
Strangely my request is timing out after a long spinner after I execute this code and I have no idea what the problem is
Thanks for all your help in advance
EDIT: I have found the source of the problem is the while loop itself, should I comment everything out as such:
while (!feof($temp_table_two) ) {
$getid = fgetcsv($temp_table_two, 1024);
// if($getid[0] >= $newImageID)
// {
// // $getid[0]++; //increase id in temp_table_two by 1 if it is > $newImageID
// echo $getid[0];
// }
}
The code still doesn't work yet the only thing left to run is the loop that doesn't do anything
EDIT 2:
Following an answer, I did away with the temp table and just work from the table itself, this if statement is executed BEFORE adding the new data with its ID
if($exists == "true") //does the $newImageID already exist in the gallery?
{
$table = fopen($fullURL,'r+');
while (!feof($table) ) {
$getid = fgetcsv($table, 1024);
if($getid[0] >= $newImageID)
{
echo $getid[0];
$getid[0]++; //increase id in temp_table_two by 1 if it is > $newImageID
}
}
fclose($table);
}
The code no longer times out, but the items inside $getid[0] are not incremented. I have echoed them and it does echo all of the ID's equal to or bigger than my $newImageID but the $getid[0]++; doesn't seem to be affecting the CSV at all
You are testing if you reach the end of the temp file and that's wrong. You need to check the origin file and also read from it!
while (!feof($table) ) {
$getid = fgetcsv($table, 1024);
Try this:
if ($csv = fopen($temp_table_two, 'r+')) do {
$getid = fgetcsv($csv, 1024);
if($getid[0] >= $newImageID)
{
echo $getid[0]; // $getid[0]++;
}
} while (!feof($csv));
That will prevent your while loop from timing out due to being stuck in an infinite if there is a problem opening the file. feof will return true only if it reaches EOF, it will return false otherwise which will cause it to never be able to break out.
For actually writing your data back to the CSV file, your current code won't work as fgetcsv just gives you an array representation of a CSV line in the file. Writing to that array just changes the array, not back to the file.
For that, see this similar answer: Append data to middle line/row of a csv instead of the last line or row
or
http://php.net/manual/en/function.fputcsv.php

how do I target column names passed in an array to construct DB query

I hope this makes some sense, I've been working with this code for some days and am currently feeling slightly dispraxic!
I have a massive form that passes loads of info into a DB, an integral part of the form concerns file uploads.
I am creating an array called uploads and passing the column names from my database in the hope that I can use them to generate a query that will automatically enter any information it recieves into the corresponding column.
Here is an example of the code for my fields:
<label>Upload copy front page of contract and relevant special condition(s)</label>
<input type="file" name="upload[UploadCopyContr]" value="<?php echo $out['UploadCopyContr']?>" />
The array is processed on the recieving end and the files are uploaded to the required folders without any issues, what I am having problems with is how to target the column string dynamically so I can produce my query string.
This would be the kind of query I would hope to end up with:
UPDATE $table SET $column = $value WHERE
So how do I extract the column name from my array and get it in a variable? If I var_dump ($_FILES["upload"]); I get the following response
["name"]=> array(4) { ["UploadCopyExtr"]=> string(9) "test2.pdf" ["UploadCopyContr"]=> string(9) "test1.pdf" ["UploadCopyTaxDesigLttr"]=> string(0) "" ["UploadOthTaxDesigDoc"]=> string(0) ""
So I know the values are there in the array, I just have no idea how to target them!
I could use $_FILES["uploads"]["UploadCopyExtr"] but thats no good for a dynamically generated query.
This is my file upload code - which is working perfectly.
$path = 'uploads/';
foreach ($_FILES["upload"]["error"] as $key => $error)
{
$tmp_name = $_FILES["upload"]["tmp_name"][$key];
{
if (!$tmp_name) continue;
}
$name = $_FILES["upload"]["name"][$key];
if ($error == UPLOAD_ERR_OK)
{
// Check if directory exists and create it if not.
if(!is_dir($path . $_POST["AccountID"] ."/". $_POST["PropertyID"])) {
mkdir($path . $_POST["AccountID"] ."/");
mkdir($path . $_POST["AccountID"] ."/" . $_POST["PropertyID"] ."/");
}
if (move_uploaded_file($tmp_name, $path . $_POST["AccountID"] ."/". $_POST["PropertyID"] ."/".$name))
$uploaded_array[] .= "Uploaded file '".$name."'.<br/>\n";
else
$errormsg .= "Could not move uploaded file '".$tmp_name."' to '".$name."'<br/>\n";
}
else $errormsg .= "Upload error. [".$error."] on file '".$name."'<br/>\n";
}
I havn't included an example of my query since I have no problems with that aspect of the problem, I just want to know how to dynamically target those column names, forgive me if the 2 are intrinsically linked, I'm happy to provide whatever information is needed.
Use array_keys to get the keys of the array. You can also use implode to combine the keys into a part of the query. Basically:
$columns = implode( ', ', array_keys( $array ) );
// This nets you 'ColumnName1, ColumnName2, Etc, Etc2'
And the same for the values. Then insert these strings into the query.
Make sure, however, that you handle any errors the DB throws at you and screen values you insert into it.
The end-user CAN manipulate form HTML, and, as such, modify the column names that will be inserted into the query.

Php WHILE loops only find one element

I got a problem with the following php code. It is supposed to list the items of a S3 bucket and find&delete files which contain a certain string in their filenames.
Problem is: only one file is deleted the others remain on the bucket after the execution of the script.
I can't find where the issue comes from so I ask you :/
$aS3Files = $s3->getBucket($bucketName); // list all elements in the bucket
$query = mysql_query("SELECT filename FROM prizes_media WHERE prize_id=" . $_POST["prizeId"]); // finds all filenames linked to the prize
while($media = mysql_fetch_array($query)){
// Find relevant files
while ( list($cFilename, $rsFileData) = each($aS3Files) ) { // reformat the bucket list into a table and reads through it
if(strpos($cFilename,$media['filename'])) {
$s3->deleteObject($bucketName, $cFilename); // deletes all files that contain $media['filename'] in their filename
}
}
}
// 2. Delete DB entry
mysql_query("DELETE FROM prizes WHERE id=" . $_POST['prizeId'] ); // deletes the entry correponding to the prize in the DB (deletes media table in cascade)
You may be getting false negatives on your if, you should be using this:
if(strpos($cFilename,$media['filename']) !== FALSE) { ...
Edit
Here is a different way to loop the bucket, based on the structure on your comment:
foreach($aS3Files as $filename => $filedata) {
if(strpos($filename, $media['filename']) !== FALSE) {
$s3->deleteObject($bucketName, $filename); // deletes all files that contain $media['filename'] in their filename
}
}

how to insert value in a particular location in csv file using php

Is it possible to write at a particular location in a CSV file using PHP?
I don't want to append data at the end of the CSV file. But I want to add data at the end of a row already having values in the CSV.
thanks in advance
No, it s not possible to insert new data in the middle of a file, due to filesystem nature.
Only append at the end is possible.
So, the only solution is to make another file, write a beginning part of source, append a new value, and then append the rest of the source file. And finally rename a resulting file to original name.
There you go. Complete working code:
<?php
//A helping function to insert data at any position in array.
function array_insert($array, $pos, $val)
{
$array2 = array_splice($array, $pos);
$array[] = $val;
$array = array_merge($array, $array2);
return $array;
}
//What and where you want to insert
$DataToInsert = '11,Shamit,Male';
$PositionToInsert = 3;
//Full path & Name of the CSV File
$FileName = 'data.csv';
//Read the file and get is as a array of lines.
$arrLines = file($FileName);
//Insert data into this array.
$Result = array_insert($arrLines, $PositionToInsert, $DataToInsert);
//Convert result array to string.
$ResultStr = implode("\n", $Result);
//Write to the file.
file_put_contents($FileName, $ResultStr);
?>
Technically Col. Shrapnel's answer is absolutely right.
Your problem is that you don't want to deal with all these file operations just to change some data. I agree with you. But you're looking for the solution in a wrong level. Put this problem in a higher level. Create a model that represents an entity in your CSV database. Modify the model's state and call its save() method. The method should be responsible to write your model's state in CSV format.
Still, you can use a CSV library that abstracts low level operations for you. For instance, parsecsv-for-php allows you to target a specific cell:
$csv = new parseCSV();
$csv->sort_by = 'id';
$csv->parse('data.csv');
# "4" is the value of the "id" column of the CSV row
$csv->data[4]['firstname'] = 'John';
$csv->save();

Categories