دمج نص في مستند

يشرح هذا الدليل كيفية استخدام Google Docs API لدمج المعلومات من مصدر بيانات خارجي واحد أو أكثر في مستند نموذج حالي.

النموذج هو نوع من المستندات يحتوي على نص ثابت وعناصر نائبة للمحتوى الديناميكي. على سبيل المثال، قد يحتوي نموذج العقد على نص ثابت مع عناصر نائبة لاسم المستلِم وعنوانه. بعد ذلك، يدمج التطبيق بيانات خاصة بالمستخدم في النموذج لإنشاء المستند النهائي.

هناك عدة أسباب تجعل هذا النهج مفيدًا:

  • يمكن للمصمّمين ضبط تصميم المستند بدقة باستخدام "مستندات Google". هذا أسهل من ضبط المَعلمات في تطبيقك لضبط التنسيق المعروض.

  • إنّ فصل المحتوى عن العرض هو مبدأ تصميم معروف وله مزايا عديدة.

مخطّط يوضّح كيفية دمج البيانات من مصدر في نموذج لإنشاء مستند
الشكل 1. دمج البيانات في نموذج لإنشاء مستند

آلية عمل دمج المستندات

في ما يلي مثال على كيفية استخدام Docs API لدمج البيانات في مستند:

  1. أنشئ مستندك باستخدام محتوى العنصر النائب لمساعدتك في التصميم والتنسيق. يتم الاحتفاظ بأي تنسيق نص تريد استبداله.

  2. لكل عنصر ستدرجه، استبدِل محتوى العنصر النائب بعلامة. احرص على استخدام سلاسل من غير المحتمل أن تظهر بشكل طبيعي. على سبيل المثال، {{account-holder-name}} قد تكون علامة جيدة.

  3. في الرمز البرمجي، استخدِم Google Drive API لإنشاء نسخة من المستند.

  4. في الرمز البرمجي، استخدِم طريقة Docs API's batchUpdate مع اسم المستند وضِّمن ReplaceAllTextRequest.

تشير معرّفات المستندات إلى مستند ويمكن استخلاصها من عنوان URL:

https://docs.google.com/document/d/DOCUMENT_ID/edit

إدارة النماذج

بالنسبة إلى مستندات النماذج التي يحدّدها التطبيق ويملكها، أنشئ النموذج باستخدام حساب مخصّص يمثّل التطبيق. تُعدّ حسابات الخدمة خيارًا جيدًا وتتجنّب المشاكل المتعلقة بسياسات Google Workspace التي تقيّد المشاركة.

عند إنشاء نُسخ من المستندات من النماذج، استخدِم دائمًا بيانات اعتماد المستخدم النهائي. يمنح ذلك المستخدمين تحكّمًا كاملاً في المستند الناتج ويمنع حدوث مشاكل في قابلية التوسّع مرتبطة بالحدود المفروضة على كل مستخدم في Google Drive.

لإنشاء نموذج باستخدام حساب خدمة، اتّبِع الخطوات التالية باستخدام بيانات اعتماد التطبيق:

  1. أنشئ مستندًا باستخدام documents.create في Docs API.
  2. عدِّل الأذونات للسماح لمستلِمي المستند بقراءته باستخدام permissions.create في Drive API.
  3. عدِّل الأذونات للسماح لمؤلّفي النماذج بالكتابة فيه باستخدام permissions.create في Drive API.
  4. عدِّل النموذج حسب الحاجة.

لإنشاء نُسخة من المستند، اتّبِع الخطوات التالية باستخدام بيانات اعتماد المستخدم:

  1. أنشئ نسخة من النموذج باستخدام files.copy في الـ Drive API.
  2. استبدِل القيم باستخدام documents.batchUpdate في Docs API.

مثال: دمج البيانات في نموذج

يوضّح عينة تعليمات برمجية التالي كيفية استبدال حقلَين في جميع علامات تبويب النموذج بقيم حقيقية لإنشاء مستند نهائي:

صورة تعرض نموذج مستند يتضمّن عناصر نائبة للعلامات والمستند الناتج بعد الدمج.
الشكل 2. استبدال العناصر النائبة للعلامات بالقيم

لإجراء عملية الدمج هذه، استخدِم الرمز البرمجي التالي:

جافا

String customerName = "Alice";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String date = formatter.format(LocalDate.now());

// Make a copy of the template document using the Drive API.
String copyTitle = "Merged Document";
File copyMetadata = new File().setName(copyTitle);
File documentCopyFile =
        driveService.files().copy(DOCUMENT_ID, copyMetadata).execute();
String documentCopyId = documentCopyFile.getId();

List requests = new ArrayList<>();
// One option for replacing all text is to specify all tab IDs.
requests.add(new Request()
        .setReplaceAllText(new ReplaceAllTextRequest()
                .setContainsText(new SubstringMatchCriteria()
                        .setText("{{customer-name}}")
                        .setMatchCase(true))
                .setReplaceText(customerName)
                .setTabsCriteria(new TabsCriteria()
                        .addTabIds(TAB_ID_1)
                        .addTabIds(TAB_ID_2)
                        .addTabIds(TAB_ID_3))));
// Another option is to omit TabsCriteria if you are replacing across all tabs.
requests.add(new Request()
        .setReplaceAllText(new ReplaceAllTextRequest()
                .setContainsText(new SubstringMatchCriteria()
                        .setText("{{date}}")
                        .setMatchCase(true))
                .setReplaceText(date)));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest();
service.documents().batchUpdate(documentCopyId, body.setRequests(requests)).execute();

Node.js

  let customerName = 'Alice';
  let date = yyyymmdd()
  let requests = [
    // One option for replacing all text is to specify all tab IDs.
    {
      replaceAllText: {
        containsText: {
          text: '{{customer-name}}',
          matchCase: true,
        },
        replaceText: customerName,
        tabsCriteria: {
          tabIds: [TAB_ID_1, TAB_ID_2, TAB_ID_3],
        },
      },
    },
    // Another option is to omit TabsCriteria if you are replacing across all tabs.
    {
      replaceAllText: {
        containsText: {
          text: '{{date}}',
          matchCase: true,
        },
        replaceText: date,
      },
    },
  ];

  // Make a copy of the template document using the Drive API.
  let copyTitle = 'Merged Document';
  driveService.files.copy({
    fileId: '1yBx6HSnu_gbV2sk1nChJOFo_g3AizBhr-PpkyKAwcTg',
    resource: {
      name: copyTitle,
    },
  }, (err, driveResponse) => {
    if (err) return console.log('The Drive API returned an error: ' + err);
    let documentCopyId = driveResponse.data.id;

    google.options({auth: auth});
    google
        .discoverAPI(
            'https://docs.googleapis.com/$discovery/rest?version=v1&key={YOUR_API_KEY}')
        .then(function(docs) {
          docs.documents.batchUpdate(
              {
                documentId: documentCopyId,
                resource: {
                  requests,
                },
              },
              (err, {data}) => {
                if (err) return console.log('The API returned an error: ' + err);
                console.log(data);
              });
        });
  });

Python

customer_name = 'Alice'
date = datetime.datetime.now().strftime("%y/%m/%d")

# Make a copy of the template document using the Drive API.
copy_title = 'Merged Document'
body = {
    'name': copy_title
}
drive_response = drive_service.files().copy(
    fileId=DOCUMENT_ID, body=body).execute()
document_copy_id = drive_response.get('id')

requests = [
        # One option for replacing all text is to specify all tab IDs.
        {
        'replaceAllText': {
            'containsText': {
                'text': '{{customer-name}}',
                'matchCase':  'true'
            },
            'replaceText': customer_name,
            'tabsCriteria': {
                'tabIds': [TAB_ID_1, TAB_ID_2, TAB_ID_3],
            },
        }},
        # Another option is to omit TabsCriteria if you are replacing across all tabs.
        {
        'replaceAllText': {
            'containsText': {
                'text': '{{date}}',
                'matchCase':  'true'
            },
            'replaceText': str(date),
        }
    }
]

result = service.documents().batchUpdate(
    documentId=document_copy_id, body={'requests': requests}).execute()

التعامل مع القوائم والجداول الديناميكية

يستخدم دمج المستندات العادي ReplaceAllTextRequest لاستبدال العناصر النائبة الفردية التي تُستخدم مرة واحدة (مثل {{customer-name}} أو {{date}}). ومع ذلك، إذا كانت بياناتك تتضمّن قائمة ديناميكية بالعناصر (مثل أسطر في فاتورة أو قائمة بالمنتجات المطلوبة أو جدول ديناميكي)، لا يمكنك استخدام عملية استبدال النص العادية لأنّ عدد العناصر غير معروف أثناء تصميم النموذج.

للتعامل مع محتوى القائمة الديناميكية، استخدِم إحدى الاستراتيجيات التالية.

الخيار 1: إلحاق صفوف بجدول نموذج

إذا كان مستند النموذج يحتوي على جدول منسَّق (على سبيل المثال، مع صف عنوان وصف واحد للعنصر النائب)، يمكنك استنساخ الصفوف وتعبئتها بشكل ديناميكي لكل عنصر في قائمتك:

  1. قراءة بنية النموذج: استخدِم طريقة documents.get للعثور على الجدول وتحديد فهرس صف النموذج.
  2. إدراج صفوف جديدة: لكل عنصر في قائمة بيانات الجمهور (باستثناء العنصر الأول الذي يمكنه إعادة استخدام صف النموذج الحالي)، استخدِم InsertTableRowRequest لإدراج صف جديد أسفل صف النموذج.
  3. تعبئة بيانات الخلية: املأ الخلايا في صف النموذج عن طريق استبدال العناصر النائبة. بالنسبة إلى الصفوف التي تم إنشاؤها حديثًا، استخدِم InsertTextRequest لإدراج النص المعني في موقع الإحداثيات لكل خلية.

للاطّلاع على أمثلة حول كيفية إدراج صفوف الجدول، راجِع العمل مع الجداول.

الخيار 2: استبدال علامة بجدول تم إنشاؤه

إذا أردت إنشاء الجدول من البداية آليًا:

  1. وضع علامة عنصر نائب: استخدِم علامة واحدة (مثل {{invoice-table}}) في مستند النموذج لتحديد مكان ظهور القائمة.
  2. تحديد موقع العنصر النائب: استخدِم عملية بحث للعثور على فهرس بداية العلامة.
  3. حذف العنصر النائب: استخدِم DeleteContentRangeRequest لإزالة النص {{invoice-table}}.
  4. إدراج الجدول: أرسِل InsertTableRequest عند فهرس البداية هذا، مع تحديد عدد الصفوف والأعمدة استنادًا إلى مصدر البيانات.
  5. كتابة القيم: املأ كل خلية في الجدول بالتسلسل.

للاطّلاع على أمثلة حول إدراج الجداول آليًا، راجِع العمل مع الجداول.