I have an array of strings and I need to append it in a spreadsheet, from the first to the n-th column according to the size of the array.
The strings could represent several type of values, e.g. numbers, dates and so on, so they must be put in the sheet as user entered to keep their meaning.
As far as I understand, the Google APIs offer two ways to append values, but none of them seems to be suitable for this case. Here's the issues I encountered:
Using Google_Service_Sheets_AppendCellsRequest
The problem with this method is that I cannot find a way to set the values as user entered. Here is a code sample:
// Build the CellData array
$values = array();
foreach( $ary_values AS $d ) {
$cellData = new Google_Service_Sheets_CellData();
$value = new Google_Service_Sheets_ExtendedValue();
$value->setStringValue($d);
$cellData->setUserEnteredValue($value);
$values[] = $cellData;
}
// Build the RowData
$rowData = new Google_Service_Sheets_RowData();
$rowData->setValues($values);
// Prepare the request
$append_request = new Google_Service_Sheets_AppendCellsRequest();
$append_request->setSheetId(0);
$append_request->setRows($rowData);
$append_request->setFields('userEnteredValue');
// Set the request
$request = new Google_Service_Sheets_Request();
$request->setAppendCells($append_request);
(full working code is here)
Even using CellData.setUserEnteredValue, the data is interpreted as string because one have to set the values using ExtendedValue.setStringValue (or the other supported methods, i.e. setBoolValue, setFormulaValue, setNumberValue).
Using spreadsheets.values.append
Using this method I can have the data rendered as user entered, but the "appending" is done in the wrong place in several cases. As stated in the documentation:
The input range is used to search for
existing data and find a "table" within that range. Values will be
appended to the next row of the table, starting with the first column
of the table.
So this method is meant to behave in a different way from the previous one, and that leads to problem for me.
This is a piece of code that should do what I want:
$body = new Google_Service_Sheets_ValueRange([
'values' => [ $array_values ]
]);
$params = [
'valueInputOption' => 'USER_ENTERED'
];
// the n-th column
$end_column = chr( ord('A') + count($array_values));
$result = $sheet_service->spreadsheets_values->append(
$fileId,
$sheetName . "!A1:" . $end_column . "1",
$body,
$params
);
Indeed, it works fine but with some exceptions, e.g.:
if the cell A1 is empty, the append method will put the values starting at column 2 (see this example to understand why).
if I have some written rows, then an empty row, then other written rows, the append method will "fill the gap" instead of putting the values after the very last written row.
Am I doing some errors? Is there a way to make what I want using either one or the other method?
To recap:
the values must be considered as user entered (e.g. a number or a date should be interpreted as such)
the values must be written in the row after the last non-empty cell, starting at column one
the append operation must be atomic (i.e. sending one request to get the last non-empty cell row and then sending another request to write data into that is not an option, because in the meantime another user could put data there)
Any advice is welcome!
Related
Sorry for my English but it is not my native language.
I have created a user interface to insert data to MySQL. Everything except one thing is ok but when I want to read data from multiple checkboxes and write them to SET type in MySQL it just doesn't work. I have tried to find the answer but after 4 hours I can't find it or I don't understand it.
http://jyxo.info/uploads/21/21b104df77f6ca723bb708d8d0549af5430e8e91.jpg
dobaVyskytu is SET type and there are in with month you can find mushroom(my tema is online atlas of mushrooms)
in user interfacei have 12 checkbox for 12 month.
http://jyxo.info/uploads/FD/fd548760b155307dfa677ada7c4be4996abf7b93.png
In dobavyskytu i need to have multiple select and that is reason why i use $doba +=
if(isset($_POST["Leden"]))
{
$doba += "Leden";
}
if(isset($_POST["Únor"]))
{
$doba += "Únor";
}
if(isset($_POST["Březen"]))
{
$doba += "Březen";
}
Db::query("INSERT INTO houby(nazev,dobaVyskytu,mistoVyskytu,popis,jedovatost,img)VALUES(?,?,?,?,?,?)",$nazev,$doba,$misto,$popis,$jedovatost,$foto);
Thank you all for reading and for help because it works now.
For strings in PHP, it uses . as concatanation not +, so
$doba .= "Leden";
Edit:
For a better way of doing this, you should try something like...
$options = [];
if(isset($_POST["Leden"]))
{
$options[] = "Leden";
}
if(isset($_POST["Únor"]))
{
$options[] = "Únor";
}
...
$doba = implode(',', $options);
As this will give you something like Leden,Únor
My hypotheses are:
$doba is the variable you want to insert in your SET type column (I translated and it seems the values you put as example in your question is Slovak for "January", "February", "March" -- I suppose there could be more).
I suppose that your SET type column is "dobaVyskytu" and that you created it correctly in MySQL by including all the possible values in the column definition.
(Your question update seem to confirm my hypotheses!)
First, when you want to insert multiple values in a SET type column in MySQL, the string value has to be separated with commas.
With the code I see, you can end up with that string "LedenÚnorBřezen" (I suppose you use += for string concatenation, but you should really use .= like Nigel Ren mentionned). You really want to end up with a string like "Leden,Únor,Březen" if all the 3 values you show are checked in your form.
See here for how to handle SET type in MySQL:
https://dev.mysql.com/doc/refman/5.7/en/set.html
Since you do not know if you will end up with 0 or multiple values for that column, I would suggest to make $doba an array.
$doba = array(); // depending on your PHP version, you can also write $doba = [];
After, you can add your values this way (the syntax $array[] = 'value' will apprend a value to the array):
$doba[] = "Leden";
$doba[] = "Únor";
$doba[] = "Březen";
Then, before inserting it, you can convert the array to a string with the values separated by commas that way:
$csvDoba = implode(',', $doba);
Then use $csvDoba instead of $doba in your Db::query() line.
After you get this working, here are more things you can look for to improve your code:
You can also take advantage PHP magic by naming your form checkbox with a special name to avoid repeating yourself.
For example, you can name all your checkboxes with the name "doba[]", and if (isset($_POST["doba"]), it will already be an array with all the checked values! But beware, if no value is checked, it won't be set. That way, you will avoid doing an if condition for each of your checkbox.
You can do something like this in your code to retrieve the value:
$doba = isset($_POST['doba']) ? (array) $_POST['doba'] : array();
What this do?
If any checkboxes named "doba[]" is checked, then you will retrieve them and make sure the value you retrieve is of type array, the "(array)" part for the value to be an array even if it was not (e.g., an error or someone trying to hack your form). Else you will return an empty array (as no choices has been put).
If you are not familar with this syntax, do a searcch for "ternary operator".
You will of course want to do some validation of your values if not already done
You might look to put the values in another table instead of using the "SET type", but that is up to you and at this stade you probably still have a couple stuff to learn, so I don't want to flood you with too much info. ;-)
Able to update the row by providing the range, here is the code:
$range = "A1:B1";
$valueRange= new Google_Service_Sheets_ValueRange();
$valueRange->setValues(["values" => ["a", "b"]]);
$conf = ["valueInputOption" => "RAW"];
$service->spreadsheets_values->update($spreadsheetId, $range, $valueRange, $conf);
Considering I don't know the range, how can I insert the row to the end of the sheet.
Are you looking for spreadsheets.values.append? The guide for example usage is here.
The reference docs for append describe it as: Appends values to a spreadsheet. The input range is used to search for existing data and find a "table" within that range. Values will be appended to the next row of the table, starting with the first column of the table. See the guide and sample code for specific details of how tables are detected and data is appended.
If that's not what you're looking for, can you be more specific with your question?
As Sam pointed out, the documentation states "range is used to search for existing data" and "Values will be appended to the next row".
Therefore, if you set the Range to the entire spreadsheet, you would get the results you desire. (Original post title says append to end of row, while your description says add a row to the end of the spreadsheet, so I assume the latter is what you want.)
Thus, set the range to the worksheet name.
$range = "Sheet1"; //your worksheet name
$valueRange= new Google_Service_Sheets_ValueRange();
$valueRange->setValues(["values" => ["a", "b"]]);
$conf = ["valueInputOption" => "RAW"];
$service->spreadsheets_values->append($spreadsheetId, $range, $valueRange, $conf);
Row will be added to the bottom of the spreadsheet.
Since I've done a project which is quite similar to the concept of what you're doing (I used JS though), I suggest checking the rows if it's empty or not using the GET request for Sheetsv4. Make it such that if the cell is empty, you'll write the appended data there using the Write on Sheets using PUT guide. This was my dynamic workaround when I didn't know the last row.
I have a column family in cassandra which records all the events emitted by a particular user over a specified time period.
I'm using a composite column consisting of a UUID1 and a UTF8 string. I'd like to select all the columns after a paticular time.
// Code to create the column family.
use phpcassa\SystemManager;
$sys = new SystemManager('127.0.0.1');
$sys->create_column_family($keyspace, 'UserActivity', array(
"comparator_type" => "CompositeType(LexicalUUIDType, UTF8Type)",
"key_validation_class" => "UTF8Type"
));
In the code below I try to read the data. Initially I tried creating an array with just the event type set at the 1 index, however although this seemed to work I got lots of errors in the log. Now, I'm trying to set a timestamp in the past and base a UUID1 on it. No errors - but no data either.
// Code to read data
$activityFam = new ColumnFamily($this->pool, 'UserActivity');
$activityFam->insert_format = ColumnFamily::ARRAY_FORMAT;
$activityFam->return_format = ColumnFamily::ARRAY_FORMAT;
$fiveMinPrev = $this->dateFactory->getDateTime();
$fiveMinPrev->sub(new \DateInterval("PT5M"));
$uuid = \phpcassa\UUID::uuid1(null, $fiveMinPrev->getTimestamp());
// Get the most recent SESSION event from the users activity log.
$slice = new ColumnSlice(array(0 => $uuid, 1=>self::EVENT_SESSION));
$columns = $activityFam->get($someUserId, $slice);
How do I achieve selecting columns from a specified time onwards?
Thanks,
Bah!
I realised this is exactly the correct approach to take however it seems I wasn't using a timestamp generated by my 'DateFactory' (which allows me to freeze and manipulate time during testing) to base the timestamp on when I actually inserted the record.
Thus producing incorrect results!
I'm creating a dynamic lead capture script.
The form passes the table name, and the rest of the post data.
I'm looking for a way to collect all the post inputs and insert that into a MySQL table without knowing the input names since each 'lead' script is different and contains different fields.
The table is already created and contains all the columns necessary for the input.
Any clean ideas?
Cheers!
A quick solution is to serialize an array of your validated post data. This will convert it into a string for storing in your Database.
You can unserialize that string to convert it back into a manageble array.
http://php.net/manual/en/function.serialize.php
The biggest downside is not having the full SQL support that you would have otherwise, by putting data into separate database fields.
I would use a combination of both techniques by putting consistant data like names, email into their own fields and unknown data into another field.
--
Try index identification (if you don't know the specific names):
$data = array_values($_POST);
$name = $data[0];
$email = $data[1];
$etc = $data[2];
--
Generate SQL string from data. Remember be vigilant with validation and ideally you should use Mysqli bind params to correcly build your query string.
foreach($_POST as $input_name => $input_value){
//do validation here
//match columns here
if($input_name=='name') $cleaned[$input_name] = $input_value;
}
$values_csv = '"'.implode('","',$cleaned).'"';
$sql = "INSERT INTO table_name VALUES ($values_csv);";
I've got a form with checkboxes set to bitwise values (CB1=1,CB2=2,CB3=4,CB4=8), and I want to store their sum in a single cell (to avoid having empty fields). The checkboxes have the same name but different values, and the data is sent to the processing php script as a serialised string, ie name=value&name=value2&name3=value3&name=&name=value5
Currently I can separate the string and get the values into the proper cells rather efficiently/easily. But, I'm wondering if there is a way to insert the first value into the cell then add subsequent values to the same cell. I imagine it would look something like this:
foreach ( $qaPairs as $pair ) {
list($question , $answer) = explode('=', $pair);
// ^ splits Q1=A1 into $question=Q1 and $answer=A1
// this mysql_query is a modified version of what I'm currently using
mysql_query("UPDATE $table SET `$question`=`$question`+'$answer' WHERE `key`='$key';") or die(mysql_error());
// $question is also the name of the column/field
} // end foreach
I dunno if it makes a difference, but: there are other datatypes (besides bitwise integers, such as text) and other form types (like textfields).
P.S. I would rather not have to somehow check if there are multiple instances of the same name and then do addition.
P.P.S. I got the UPDATE idea from this question: MySQL Batch increase value?, and I tried it out, but it didn't update null values (which I need it to do).
Thanks in advance!