I need an example of how to modify an existing document with existing text in Google Docs via API. The documentation only shows how to insert and delete text, but not how to update. Have been looking frantically on the web to find examples or a direction on how to do it but without luck.
How to MODIFY a Google Docs document via API using search-and-replace?
Asked Answered
Finally figured it out myself.
First, follow this video to prepare authentication to the Google Docs API (even though it's about Google Sheets but the process is basically the same). Basically it consists of these steps:
- create project in Google Developer Console
- enable Google Docs API
- create credentials, including a service account for programmatic access
- share your document with the service account client email address
- install Google API's PHP client:
composer require google/apiclient
Then create a script like the following:
require_once(__DIR__ .'/vendor/autoload.php');
$client = new \Google_Client();
$client->setApplicationName('Some name'); //this name doesn't matter
$client->setScopes([\Google_Service_Docs::DOCUMENTS]);
$client->setAccessType('offline');
$client->setAuthConfig(__DIR__ .'/googleapi-credentials.json'); //see https://www.youtube.com/watch?v=iTZyuszEkxI for how to create this file
$service = new \Google_Service_Docs($client);
$documentId = 'YOUR-DOCUMENT-ID-GOES-HERE'; //set your document ID here, eg. "j4i1m57GDYthXKqlGce9WKs4tpiFvzl1FXKmNRsTAAlH"
$doc = $service->documents->get($documentId);
// Collect all pieces of text (see https://developers.google.com/docs/api/concepts/structure to understand the structure)
$allText = [];
foreach ($doc->body->content as $structuralElement) {
if ($structuralElement->paragraph) {
foreach ($structuralElement->paragraph->elements as $paragraphElement) {
if ($paragraphElement->textRun) {
$allText[] = $paragraphElement->textRun->content;
}
}
}
}
// Go through and create search/replace requests
$requests = $textsAlreadyDone = $forEasyCompare = [];
foreach ($allText as $currText) {
if (in_array($currText, $textsAlreadyDone, true)) {
// If two identical pieces of text are found only search-and-replace it once - no reason to do it multiple times
continue;
}
if (preg_match_all("/(.*?)(dogs)(.*?)/", $currText, $matches, PREG_SET_ORDER)) {
//NOTE: for simple static text searching you could of course just use strpos()
// - and then loop on $matches wouldn't be necessary, and str_replace() would be simplified
$modifiedText = $currText;
foreach ($matches as $match) {
$modifiedText = str_replace($match[0], $match[1] .'cats'. $match[3], $modifiedText);
}
$forEasyCompare[] = ['old' => $currText, 'new' => $modifiedText];
$replaceAllTextRequest = [
'replaceAllText' => [
'replaceText' => $modifiedText,
'containsText' => [
'text' => $currText,
'matchCase' => true,
],
],
];
$requests[] = new \Google_Service_Docs_Request($replaceAllTextRequest);
}
$textsAlreadyDone[] = $currText;
}
// you could dump out $forEasyCompare to see the changes that would be made
$batchUpdateRequest = new \Google_Service_Docs_BatchUpdateDocumentRequest(['requests' => $requests]);
$response = $service->documents->batchUpdate($documentId, $batchUpdateRequest);
This is my way - easy one
public function replaceText($search, $replace)
{
$client = $this->getClient();
$service = new \Google_Service_Docs($client);
$documentId = ''; // Put your document ID here
$e = new \Google_Service_Docs_SubstringMatchCriteria();
$e->text = "{{".$search."}}";
$e->setMatchCase(false);
$requests[] = new \Google_Service_Docs_Request(array(
'replaceAllText' => array(
'replaceText' => $replace,
'containsText' => $e
),
));
$batchUpdateRequest = new \Google_Service_Docs_BatchUpdateDocumentRequest(array(
'requests' => $requests
));
$response = $service->documents->batchUpdate($documentId, $batchUpdateRequest);
}
Works perfectly and across complex items like tables (unlike the accepted answer) –
Gustaf
© 2022 - 2024 — McMap. All rights reserved.