I need to get the google sheet data and then to download it as csv. but the issue is I need to get data by sheet id in the url.
I'm using this package "google/apiclient": "^2.0"
Code
$sheets = new \Google_Service_Sheets($client);
//get spreadsheet id from db
$google_sheet_link = TeamGoogleSheet::where('team_id',$team_id)->first();
$url_array = parse_url($google_sheet_link->url);
$path_array = explode("/",$url_array["path"]);
$spreadsheetId = $path_array[3];
\Log::info('Spreadsheet id');
\Log::info($spreadsheetId);
$range = 'Sheet1';
$rows = $sheets->spreadsheets_values->get($spreadsheetId, $range, ['majorDimension' => 'ROWS']);
if (isset($rows['values'])) {
$filename = storage_path("chat_bots.csv");
if (file_exists($filename))
unlink($filename);
$handle = fopen($filename, 'a');
foreach ($rows['values'] as $key => $data) {
fputcsv($handle, $data);
}
fclose($handle);
$headers = array(
'Content-Type' => 'text/csv',
);
return \Response::download($filename, 'chat_bots.csv', $headers);
}
But this requires the sheet name(range attribute) and also only 1 sheet.I want to make it dynamic. we can get sheetid from url, but didn't find a method to retrieve data by passing this sheet id.
$spreadsheet_data = $sheets->spreadsheets->get($spreadsheetId);
//get sheet titles
$work_sheets = [];
$gid_sheet = '';
foreach($spreadsheet_data->getSheets() as $s) {
$work_sheets[] = $s['properties']['title'];
}
if(preg_match("/[#&]gid=([0-9]+)/", $google_sheet_link->url)){
$explode_array = explode("#gid=",$google_sheet_link->url);
$gid = $explode_array[1];
foreach($spreadsheet_data->getSheets() as $s) {
if($s['properties']['sheetId'] == $gid)
$gid_sheet = $s['properties']['title'];
}
}
if($gid_sheet != ''){
$range = $gid_sheet;
}
else{
$range = $work_sheets[0];
}
Related
I've set up Google Sheets as a simple backend using the api for a php website. It is for a foreign language learning resource, so people can add / edit sentences.
Screenshot of the google sheet
Screenshot of Site generated from sheet
At the top of the template.php that generates each language's page I have this script, then I loop over $sheetsValues to make the table on the site.
<?php
require __DIR__ . '/vendor/autoload.php';
// connect to API
$client = new \Google_Client();
$client->setApplicationName('12Sentences');
$client->setScopes([\Google_Service_Sheets::SPREADSHEETS]);
$client->setAccessType('offline');
$client->setAuthConfig(__DIR__ . '/credentials.json');
$service = new Google_Service_Sheets($client);
$spreadsheetId = 'MY_SPREADSHEET_ID';
$range = "$language!B3:E15";
$response = $service->spreadsheets_values->get($spreadsheetId, $range );
$sheetsValues = $response->getValues();
?>
Though it doesn't get the color and formatting information from the sheet. I'd like to get the text color from the google sheet to show on the site (like the red of 'La Pomme') - as it can be used to indicate elements of the sentences (subject, verb, object order etc.) Is this possible with google sheets api?
Thanks,
Thomas
Updated code: Used Tanaike's solution below in function getRes() to get color data from Google Sheets API. Then getFormattedHtml() is called to get colored text in HTML and CSS. Not elegant but it works well for my use.
<?php
function getFormattedHtml($row, $column, $res) {
$formattedHtml = "";
// get plain text
$plain_text = $res[$row][$column]['formattedValue'];
// get textFormatRuns
$textFormatRuns = $res[$row][$column]['textFormatRuns'];
// loop over the textFormatRuns
$len = count($textFormatRuns);
for ($i=0; $i < $len; $i++) {
$currentRunStart = $textFormatRuns[$i]['startIndex'];
$currentRunEnd = $textFormatRuns[$i + 1]['startIndex'];
$substring = "";
if ($i == $len - 1) {
$substring = substr($plain_text, $currentRunStart);
} else {
$substring = substr($plain_text, $currentRunStart, $currentRunEnd - $currentRunStart);
}
$span = "";
if (isset($textFormatRuns[$i]['format']['foregroundColor'])) {
$red = $textFormatRuns[$i]['format']['foregroundColor']['red'] * 255;
$green = $textFormatRuns[$i]['format']['foregroundColor']['green'] * 255;
$blue = $textFormatRuns[$i]['format']['foregroundColor']['blue'] * 255;
$span = "<span style=\"color:rgb($red, $green, $blue)\">$substring</span>";
} else {
$span = "<span>$substring</span>";
}
$formattedHtml .= $span;
}
return($formattedHtml);
}
function getRes() {
require __DIR__ . '/vendor/autoload.php';
// connect to API
$client = new \Google_Client();
$client->setApplicationName('12Sentences');
$client->setScopes([\Google_Service_Sheets::SPREADSHEETS]);
$client->setAccessType('offline');
$client->setAuthConfig(__DIR__ . '/credentials.json');
$service = new Google_Service_Sheets($client);
$spreadsheetId = 'MY_SPREADSHEET_ID';
$range = "$language!B3:E15";
// This script uses the method of "spreadsheets.get".
$sheets = $service->spreadsheets->get($spreadsheetId, ["ranges" => [$range], "fields" => "sheets"])->getSheets();
// Following script is a sample script for retrieving "textFormat" and "textFormatRuns".
$data = $sheets[0] -> getData();
$startRow = $data[0] -> getStartRow();
$startColumn = $data[0] -> getStartColumn();
$rowData = $data[0] -> getRowData();
$res = array();
foreach ($rowData as $i => $row) {
$temp = array();
foreach ($row -> getValues() as $j => $value) {
$tempObj = [
"row" => $i + 1 + $startRow,
"column" => $j + 1 + $startColumn
];
if (isset($value['formattedValue'])) {
$tempObj['formattedValue'] = $value -> getFormattedValue();
} else {
$tempObj['formattedValue'] = "";
}
$userEnteredFormat = $value -> getUserEnteredFormat();
if (isset($userEnteredFormat['textFormat'])) {
$tempObj['textFormat'] = $userEnteredFormat -> getTextFormat();
} else {
$tempObj['textFormat'] = null;
}
if (isset($value['textFormatRuns'])) {
$tempObj['textFormatRuns'] = $value -> getTextFormatRuns();
} else {
$tempObj['textFormatRuns'] = null;
}
array_push($temp, $tempObj);
}
array_push($res, $temp);
}
return($res);
}
?>
I believe your goal as follows.
You want to retrieve the font color of the part of text in a cell.
You want to achieve this using googleapis for php.
You have already been able to get values from Google Spreadsheet using Sheets API.
Modification points:
In your script, the method of "spreadsheets.values.get" in Sheets API is used. In this case, the values of "textFormat" and "textFormatRuns" are not included. In order to retrieve those values of the part of text in a cell, it is required to use the method of "spreadsheets.get".
When above points are reflected to your script, it becomes as follows.
Modified script:
$service = new Google_Service_Sheets($client);
$spreadsheetId = 'MY_SPREADSHEET_ID';
$range = "$language!B3:E15";
// This script uses the method of "spreadsheets.get".
$sheets = $service->spreadsheets->get($spreadSheetId, ["ranges" => [$range], "fields" => "sheets"])->getSheets();
// Following script is a sample script for retrieving "textFormat" and "textFormatRuns".
$data = $sheets[0] -> getData();
$startRow = $data[0] -> getStartRow();
$startColumn = $data[0] -> getStartColumn();
$rowData = $data[0] -> getRowData();
$res = array();
foreach ($rowData as $i => $row) {
$temp = array();
foreach ($row -> getValues() as $j => $value) {
$tempObj = [
"row" => $i + 1 + $startRow,
"column" => $j + 1 + $startColumn
];
if (isset($value['formattedValue'])) {
$tempObj['formattedValue'] = $value -> getFormattedValue();
} else {
$tempObj['formattedValue'] = "";
}
$userEnteredFormat = $value -> getUserEnteredFormat();
if (isset($userEnteredFormat['textFormat'])) {
$tempObj['textFormat'] = $userEnteredFormat -> getTextFormat();
} else {
$tempObj['textFormat'] = null;
}
if (isset($value['textFormatRuns'])) {
$tempObj['textFormatRuns'] = $value -> getTextFormatRuns();
} else {
$tempObj['textFormatRuns'] = null;
}
array_push($temp, $tempObj);
}
array_push($res, $temp);
}
print($res);
Result:
When above script is used for the following situation,
the following result is obtained.
[
[
{
"row":1,
"column":1,
"formattedValue":"sample1 sample2 sample3 sample4 sample5",
"textFormat":{"bold":true},
"textFormatRuns":[
{"startIndex":8,"format":{"foregroundColor":{"red":1},"foregroundColorStyle":{"rgbColor":{"red":1}}}},
{"startIndex":15,},
{"startIndex":16,"format":{"foregroundColor":{"green":1},"foregroundColorStyle":{"rgbColor":{"green":1,}}}},
{"startIndex":23,},
{"startIndex":24,"format":{"foregroundColor":{"blue":1},"foregroundColorStyle":{"rgbColor":{"blue":1,}}}},
{"startIndex":31,}]
}
]
]
From above result, for example, it is found that sample2 of the index from 8 to 15 has the red color.
About the color of rgbColor in above result, you can see the detail information at the official document. Ref
Note:
This is a simple sample script. So please modify above for your actual situation.
Reference:
Method: spreadsheets.get
Please I am trying to import a data in a csv file to my database. the system successfully uploads some of the data and throw unknown error during the upload process. Someone should kindly assist me.
*** Controller.php***
$this->validate($request, [
'file' => 'required',
]);
$upload = $request->file('file');
$filePath = $upload->getRealPath();
$file = fopen($filePath, 'r');
$header = fgetcsv($file);
$escapedHeader = [];
// data validation
foreach ($header as $key => $value) {
$escapedItem = strtolower($value);
array_push($escapedHeader, $escapedItem);
}
//looping through colums to get data
while ($columns = fgetcsv($file)) {
if ($columns[0] == "") {
continue;
}
//trim data
foreach ($columns as $key => &$value) {
$value = ucwords($value);
}
$data = array_combine($escapedHeader, $columns);
$name = $data['name'];
$contact = $data['contact'];
$email = $data['email'];
$role = $data['role'];
$interview_date = $data['interview_date'];
$appdata = new Applicant;
$appdata->name = $name;
$appdata->contact = $contact;
$appdata->email = $email;
$appdata->role = $role;
$appdata->interview_date = $interview_date;
$appdata->save();
}
return back()->with('status', 'Applicant Data Loaded Successfully');
Below is the error showing after importing some of the data from the csv file.
I'm using Phalcon 3.0.4. I made a foreach on each file inside my folder. Currently I have just 4000 files. I did a findFirst to check if the filename already exist in MySQL (I have 100 000 rows in my table). But when I use findFirst, the response is super slow (I have to wait 20 minutes to get a response). Here is my code :
$dir = new FilesystemIterator("files/path/to/my/files/");
foreach ($dir as $file) {
if ($file->getExtension() == 'json') {
$filename = $file->getFilename();
$explode_filename = explode("_", $filename);
$date = $explode_filename[0];
$unformatted_date = DateTime::createFromFormat("Ymd-His", $date);
$date_server = $unformatted_date->format("Y-m-d H:i:s");
$timestamp_app = $explode_filename[2];
$date_app = date("Y-m-d H:i:s", $timestamp_app/1000);
echo $date_server;
$json_data = json_decode(file_get_contents($file), true);
$scan = Scans::findFirst(array(
"name = :name:",
"bind" => array("name" => $filename)
));
if (!$scan) {
...
}
}
}
I tried to make my query with the QueryBuilder PHQL but I have the same result:
$scan = $this->modelsManager->createBuilder()
->from("Scans")
->where("name = :name:", ["name" => $filename])
->limit(1)
->getQuery()
->execute();
If I remove the findFirst or queryBuilder the response is ~30ms but with the findFirst it will takes ~20 minutes... How can I do to increase the performance of the search in my table ?
By changing your code to better performing one:
$dir = new FilesystemIterator("files/path/to/my/files/");
$fileNames = [];
foreach ($dir as $file) {
if ($file->getExtension() == 'json') {
$filename = $file->getFilename();
$explode_filename = explode("_", $filename);
$date = $explode_filename[0];
$unformatted_date = DateTime::createFromFormat("Ymd-His", $date);
$date_server = $unformatted_date->format("Y-m-d H:i:s");
$timestamp_app = $explode_filename[2];
$date_app = date("Y-m-d H:i:s", $timestamp_app/1000);
echo $date_server;
$json_data = json_decode(file_get_contents($file), true);
// save the above data to some arrays
$fileNames[] = $fileName;
}
}
$scans = Scans::find([
'columns' => 'check only columns you need, otherwise you will have full models with hydration',
'conditions' => 'name IN ({fileNames:array})',
'group' => 'name',
'bind' => [
'fileNames' => $fileNames
]
]);
foreach($fileNames as $fileName) {
$filteredScans = $scans->filter(function($scan) use ($fileName) {
return $scan->name == $fileName;
}
if(!$filteredScans) {
// do here whatever
}
}
This solution can be memory heavy though, then you could include here some paginations like do some limit like proper for and do 100-10000 rows at once depending how much RAM you have.
create index on Scans.name
use group by Scans.name (if not uniq)
set some columns then be use
I am using parse_ini_file to parse an ini file using PHP.
Now I first upload an INI file to my server then open it and allow user to mak custom changes to the file.
Now once users has edited the file i get the data from post and save the file back to server. But Now i dont get my sections. INIdetails,Dialplan in the updated file so when i also want to write that to file how to do that?
This is the code :
$this->data['parameters'] = parse_ini_file($path.$filename,true);
/*Getting Our POST DATA from View*/
$data = array(
'SipUserName' => $this->input->post('SipUserName') ,
'SipAuthName' => $this->input->post('SipAuthName'),
'DisplayName' => $this->input->post('DisplayName'),
'Password' => $this->input->post('Password'),
'Domain' => $this->input->post('Domain'),
'Proxy' => $this->input->post('Proxy'),
'Port' => $this->input->post('Port'),
'ServerMode' => $this->input->post('ServerMode'),
'Param_1' => $this->input->post('Param_1'),
'Param_2' => $this->input->post('Param_2')
);
/*Creating New file with the name of customer loggedin*/
$name = $this->session->userdata('username');
$ext = $this->session->userdata('extension');
$custom_file = fopen('uploads/'.$name.'_'.$ext.'.ini', 'w');
fwrite($custom_file, "[INIDetails]\n");
foreach ($data as $key => $value)
{
fwrite($custom_file, " $key = $value\n");
}
fclose($custom_file);
/*Setting path to New CUSTOM file with customer name as prefix*/
$file = $path.$custom_file;
function write_php_ini($array, $file)
{
$res = array();
foreach($array as $key => $val)
{
if(is_array($val))
{
$res[] = "[$key]";
foreach($val as $skey => $sval) $res[] = "$skey = ".(is_numeric($sval) ? $sval : '"'.$sval.'"');
}
else $res[] = "$key = ".(is_numeric($val) ? $val : '"'.$val.'"');
}
safefilerewrite($file, implode("\r\n", $res));
}
function safefilerewrite($fileName, $dataToSave)
{ if ($fp = fopen($fileName, 'w'))
{
$startTime = microtime(TRUE);
do
{ $canWrite = flock($fp, LOCK_EX);
// If lock not obtained sleep for 0 - 100 milliseconds, to avoid collision and CPU load
if(!$canWrite) usleep(round(rand(0, 100)*1000));
} while ((!$canWrite)and((microtime(TRUE)-$startTime) < 5));
//file was locked so now we can store information
if ($canWrite)
{ fwrite($fp, $dataToSave);
flock($fp, LOCK_UN);
}
fclose($fp);
}
}
/*Creates ini file, dumps array to string and creates .INI file*/
write_php_ini($data,$file);
The culprit from your previous code is that your array is not formatted correctly, it should be array of arrays to achieve what you want.
Try below code:
// First read the ini file that the user was editing
// Your idea to read the existing ini file is good, since it will generate you the structured array
$previous_data = parse_ini_file($path . $filename, true);
// Overwrite/edit the previous_data using user's post data
$previous_data['INIDetails']['SipUserName'] = $this->input->post('SipUserName');
$previous_data['INIDetails']['Password'] = $this->input->post('Password');
$previous_data['INIDetails']['Domain'] = $this->input->post('Domain');
$previous_data['INIDetails']['Proxy'] = $this->input->post('Proxy');
$previous_data['INIDetails']['Port'] = $this->input->post('Port');
$previous_data['INIDetails']['SipAuthName'] = $this->input->post('SipAuthName');
$previous_data['INIDetails']['DisplayName'] = $this->input->post('DisplayName');
$previous_data['INIDetails']['ServerMode'] = $this->input->post('ServerMode');
$previous_data['INIDetails']['UCServer'] = $this->input->post('UCServer');
$previous_data['INIDetails']['UCUserName'] = $this->input->post('UCUserName');
$previous_data['INIDetails']['UCPassword'] = $this->input->post('UCPassword');
$previous_data['DialPlan']['DP_Exception'] = $this->input->post('DP_Exception');
$previous_data['DialPlan']['DP_Rule1'] = $this->input->post('DP_Rule1');
$previous_data['DialPlan']['DP_Rule2'] = $this->input->post('DP_Rule2');
$previous_data['DialPlan']['OperationMode'] = $this->input->post('OperationMode');
$previous_data['DialPlan']['MutePkey'] = $this->input->post('MutePkey');
$previous_data['DialPlan']['Codec'] = $this->input->post('Codec');
$previous_data['DialPlan']['PTime'] = $this->input->post('PTime');
$previous_data['DialPlan']['AudioMode'] = $this->input->post('AudioMode');
$previous_data['DialPlan']['SoftwareAEC'] = $this->input->post('SoftwareAEC');
$previous_data['DialPlan']['EchoTailLength'] = $this->input->post('EchoTailLength');
$previous_data['DialPlan']['PlaybackBuffer'] = $this->input->post('PlaybackBuffer');
$previous_data['DialPlan']['CaptureBuffer'] = $this->input->post('CaptureBuffer');
$previous_data['DialPlan']['JBPrefetchDelay'] = $this->input->post('JBPrefetchDelay');
$previous_data['DialPlan']['JBMaxDelay'] = $this->input->post('JBMaxDelay');
$previous_data['DialPlan']['SipToS'] = $this->input->post('SipToS');
$previous_data['DialPlan']['RTPToS'] = $this->input->post('RTPToS');
$previous_data['DialPlan']['LogLevel'] = $this->input->post('LogLevel');
// Set Name of New file with the name of customer logged in
$name = $this->session->userdata('username');
$ext = $this->session->userdata('extension');
$custom_file = "$name_$ext.ini";
$new_filename = $path . $custom_file;
// Write the INI file
write_php_ini($data, $new_filename);
function write_php_ini($array, $new_filename)
{
$res = array();
foreach ($array as $key => $val) {
if (is_array($val)) {
$res[] = "[$key]";
foreach ($val as $skey => $sval) {
$res[] = "$skey = " . (is_numeric($sval) ? $sval : '"' . $sval . '"');
}
} else {
$res[] = "$key = " . (is_numeric($val) ? $val : '"' . $val . '"');
}
}
safefilerewrite($new_filename, implode("\r\n", $res));
}
function safefilerewrite($new_filename, $dataToSave)
{
if ($fp = fopen($new_filename, 'w')) {
$startTime = microtime(true);
do {
$canWrite = flock($fp, LOCK_EX);
// If lock not obtained sleep for 0 - 100 milliseconds, to avoid collision and CPU load
if (!$canWrite) {
usleep(round(rand(0, 100) * 1000));
}
} while ((!$canWrite) and ((microtime(true) - $startTime) < 5));
//file was locked so now we can store information
if ($canWrite) {
fwrite($fp, $dataToSave);
flock($fp, LOCK_UN);
}
fclose($fp);
}
}
From your previous code, there are bunch of inappropriate codes which I remove. Also, too many inconsistencies like Method naming, variable naming etc.
If your function was named Camel cased then through out your code it must be named as camel-cased. If your variables are with underscore, then through out your code, they must have underscore for two or more worded variable.
I didn't edit the naming convention of your code so you won't be confused but i suggest to have a consistent naming convention through out your project.
UPDATED:
based on your answer, it seems like you changed your whole code. I would like to provide another way using nested foreach and passing by reference that save couple of lines:
$this->data['params'] = $this->parameter_m->get();
$this->data['parameters'] = parse_ini_file($path . $filename, true);
foreach ($this->data['parameters'] as $key_header => &$value_header) {
foreach ($value_header as $key_item => &$value_item) {
$value_item = $this->input->post($key_item);
}
}
$this->load->helper('file');
$this->load->library('ini');
$file = $path . $filename;
$ini = new INI($file);
$ini->read($file);
$ini->write($file, $this->data['parameters']);
Finally i got an answer:
I will loop through what i get from POST and will get each array's key. And then i will give the resultant to my Write Method
$this->data['params'] = $this->parameter_m->get();
/*Getting the parameters to display on view*/
$this->data['parameters'] = parse_ini_file($path.$filename,true);
while (current($this->data['parameters']) )
{
$param_set = current($this->data['parameters']);
$param_type = key($this->data['parameters']);
foreach ($param_set as $key =>$value)
{
$this->data['parameters'][$param_type][$key] = $this->input->post($key);
}
next($this->data['parameters']);
}
$this->load->helper('file');
$this->load->library('ini');
$file = $path.$filename;
$ini = new INI($file);
$ini->read($file);
$ini->write($file, $this->data['parameters']);
I am trying to decode a collection of json files to a mysql database and return the decoded values to a datatable for presentation. I have one table called ec2_instances, and want to send to that table an array of values which are located at cfi configuration which works fine. but have now added a new column called aws account id which is on object rather than an array I have updated the model to include the new column but I am struggling
<?php
function from_camel_case($input)
{
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
$ret = $matches[0];
foreach ($ret as &$match)
{
$match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
}
return implode('_', $ret);
}
$resource_types = array();
$resource_types['AWS::EC2::Instance'] = 'EC2Instance';
$resource_types['AWS::EC2::NetworkInterface'] = 'EC2NetworkInterface';
$resource_types['AWS::EC2::VPC'] = 'VPC';
$resource_types['AWS::EC2::Volume'] = 'Volume';
$resource_types['AWS::EC2::SecurityGroup'] = 'EC2SecurityGroup';
$resource_types['AWS::EC2::Subnet'] = 'Subnet';
$resource_types['AWS::EC2::RouteTable'] = 'RouteTable';
$resource_types['AWS::EC2::EIP'] = 'EIP';
$resource_types['AWS::EC2::NetworkAcl'] = 'NetworkAcl';
$resource_types['AWS::EC2::InternetGateway'] = 'InternetGateway';
$accounts = DB::table('aws_account')->get();
$account_id = array($accounts);
$account_id_exists = array_add($account_id, 'key', 'value');
foreach(glob('../app/views/*.json') as $filename)
{
//echo $filename;
$data = file_get_contents($filename);
if($data!=null)
{
$decoded=json_decode($data,true);
if(isset($decoded["Message"]))
{
//echo "found message<br>";
$message= json_decode($decoded["Message"]);
if(isset($message->configurationItem))
{
// echo"found cfi<br>";
$insert_array = array();
$cfi = $message->configurationItem;
switch ($cfi->configurationItemStatus)
{
case "ResourceDiscovered":
//echo"found Resource Discovered<br>";
if (array_key_exists($cfi->resourceType,$resource_types))
{
//var_dump($cfi->resourceType);
$resource = new $resource_types[$cfi->resourceType];
foreach ($cfi->configuration as $key => $value)
{
if (in_array($key,$resource->fields))
{
$insert_array[from_camel_case($key)] = $value;
}
}
if (array_key_exists($cfi->awsAccountId,$resource_types))
{
$resource = new $resource_types[$cfi->awsAccountId];
foreach ($cfi->awsAccountId as $key => $value)
{
if (in_array($key,$resource->fields))
{
$insert_array[from_camel_case($key)] = $value;
}
}
$resource->populate($insert_array);
if (!$resource->checkExists())
{
$resource->save();