1

So I created a Spreadsheet class that is a combination of a few solutions I found online for accessing Google Sheets API with PHP. It works.

class Spreadsheet {
    private $token;
    private $spreadsheet;
    private $worksheet;
    private $spreadsheetid;
    private $worksheetid;

    private $client_id = '<client id>';
    private $service_account_name = '<service_account>';  // email address
    private $key_file_location = 'key.p12'; //key.p12

    private $client;
    private $service;

    public function __construct() {
        $this->client = new Google_Client();
        $this->client->setApplicationName("Sheets API Testing");
        $this->service = new Google_Service_Drive($this->client);
        $this->authenticate();
    }
    public function authenticate()
    {
        if (isset($_SESSION['service_token'])) {
            $this->client->setAccessToken($_SESSION['service_token']);
        }
        $key  = file_get_contents($this->key_file_location);
        $cred = new Google_Auth_AssertionCredentials(
            $this->service_account_name,
            array('https://www.googleapis.com/auth/drive', 'https://spreadsheets.google.com/feeds'),            $key
        );
        $this->client->setAssertionCredentials($cred);
        if ($this->client->getAuth()->isAccessTokenExpired()) {
            $this->client->getAuth()->refreshTokenWithAssertion($cred);
        }
        $_SESSION['service_token'] = $this->client->getAccessToken();

        // Get access token for spreadsheets API calls
        $resultArray = json_decode($_SESSION['service_token']);
        $this->token = $resultArray->access_token;
    }

    public function setSpreadsheet($title) {
        $this->spreadsheet = $title;
        return $this;
    }
    public function setSpreadsheetId($id) {
        $this->spreadsheetid = $id;
        return $this;
    }
    public function setWorksheet($title) {
        $this->worksheet = $title;
        return $this;
    }
    public function insert() {
        if (!empty($this->token)) {
            $url = $this->getPostUrl();
        } else {
            echo "Authentication Failed";
        }
    }
    public function add($data) {
        if(!empty($this->token)) {
            $url = $this->getPostUrl();
            if(!empty($url)) {
                $columnIDs = $this->getColumnIDs();
                if($columnIDs) {
                    $fields = '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gsx="http://schemas.google.com/spreadsheets/2006/extended">';
                    foreach($data as $key => $value) {
                        $key = $this->formatColumnID($key);
                        if(in_array($key, $columnIDs)) {
                            $fields .= "<gsx:$key><![CDATA[$value]]></gsx:$key>";
                        }
                    }
                    $fields .= '</entry>';

                    $headers = [
                        "Authorization" => "Bearer $this->token", 
                        'Content-Type' => 'application/atom+xml'
                    ];
                    $method = 'POST';
                    $req = new Google_Http_Request($url, $method, $headers, $fields);
                    $curl = new Google_IO_Curl($this->client);
                    $results = $curl->executeRequest($req);
                    var_dump($results);
                }
            }
        }
    }
    private function getColumnIDs() {
        $url = "https://spreadsheets.google.com/feeds/cells/" . $this->spreadsheetid . "/" . $this->worksheetid . "/private/full?max-row=1";
        $headers = array(
            "Authorization" => "Bearer $this->token",
            "GData-Version: 3.0"
        );

        $method = "GET";
        $req = new Google_Http_Request($url, $method, $headers);
        $curl = new Google_IO_Curl($this->client);
        $results = $curl->executeRequest($req);

        if($results[2] == 200) {

            $columnIDs = array();
            $xml = simplexml_load_string($results[0]);
            if($xml->entry) {
                $columnSize = sizeof($xml->entry);
                for($c = 0; $c < $columnSize; ++$c) {
                    $columnIDs[] = $this->formatColumnID($xml->entry[$c]->content);
                }
            }
            return $columnIDs;
        }
        return "";
    }
    private function getPostUrl() {
        if (empty($this->spreadsheetid)){

            #find the id based on the spreadsheet name
            $url = "https://spreadsheets.google.com/feeds/spreadsheets/private/full?title=" . urlencode($this->spreadsheet);
            $method = 'GET';
            $headers = ["Authorization" => "Bearer $this->token"];
            $req = new Google_Http_Request($url, $method, $headers);
            $curl = new Google_IO_Curl($this->client);
            $results = $curl->executeRequest($req);          

            if($results[2] == 200) {
                $spreadsheetXml = simplexml_load_string($results[0]);
                if($spreadsheetXml->entry) {
                    $this->spreadsheetid = basename(trim($spreadsheetXml->entry[0]->id));
                    $url = "https://spreadsheets.google.com/feeds/worksheets/" . $this->spreadsheetid . "/private/full";
                    if(!empty($this->worksheet)) {
                        $url .= "?title=" . $this->worksheet;
                    }
                    $req = new Google_Http_Request($url, $method, $headers);
                    $response = $curl->executeRequest($req);

                    if($response[2] == 200) {
                        $worksheetXml = simplexml_load_string($response[0]);
                        if($worksheetXml->entry) {
                            $this->worksheetid = basename(trim($worksheetXml->entry[0]->id));
                        }
                    }
                }
            }           
        }


        if(!empty($this->spreadsheetid) && !empty($this->worksheetid)) {
            return "https://spreadsheets.google.com/feeds/list/" . $this->spreadsheetid . "/" . $this->worksheetid . "/private/full";
        }

        return "";
    }
    private function formatColumnID($val) {
        return preg_replace("/[^a-zA-Z0-9.-]/", "", strtolower($val));
    }
}

I then use this test php file to add rows to to my spreadsheet:

$Spreadsheet = new Spreadsheet();
$Spreadsheet->
    setSpreadsheet("test spreadsheet")->
    setWorksheet("Sheet1")->
    add(array("name" => "Cell 1", "email" => "Cell 2"));

With this I can delete a row / update a row and append a row. However, the MAIN reason I needed this was to INSERT a row. Has anyone figured out a way to do this? Any language is fine although id prefer a php solution.

Bill Garrison
  • 2,226
  • 3
  • 34
  • 75
  • 1
    Have you seen the documentation for "Adding a List Row", in the Sheets API? Look under the "Protocol" tab for the and see the POST request. [Google Documentation](https://developers.google.com/google-apps/spreadsheets/#adding_a_list_row_1) – Alan Wells Jul 31 '15 at 17:27
  • 1
    "The API inserts the new row immediately after the last row that appears in the list feed, which is to say immediately before the first entirely blank row." I am looking to insert a row in the middle of a spreadsheet. – Bill Garrison Aug 03 '15 at 14:14
  • But when I look at the [v4 docs](https://developers.google.com/sheets/api/guides/authorizing), it says that I can't use a service account to do this. It says that I can interact with an individual's private user data only with OAuth 2.0 tokens. How did you get this to work? – Volomike Feb 19 '17 at 07:09

1 Answers1

1

You can call an Apps Script stand alone script from PHP using an HTTPS GET or POST request. PHP can make a GET or POST request, and Apps Script can obviously insert the row anywhere using SpreadsheetApp service. You'll probably want to use Content Service also inside of the Apps Script code to get a return confirmation back that the code completed.

You might want to use a POST request for better security. So, again, you can use Apps Script as an intermediary between your PHP and your spreadsheet. The doPost() in the Apps Script file will need an event handler, normally assigned to the letter "e":

doPost(e) {
  //Get e and retrieve what the code should do

  //Insert the row

};

Also, see this answer:

Stackoverflow - Call a custom GAS function from external URL

Community
  • 1
  • 1
Alan Wells
  • 30,746
  • 15
  • 104
  • 152