olamilekan/laravel-google-sheets 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

olamilekan/laravel-google-sheets

Composer 安装命令:

composer require olamilekan/laravel-google-sheets

包简介

A Laravel package for working with Google Sheets API with support for multiple spreadsheet connections

README 文档

README

A fluent Laravel package for reading, writing, and managing Google Sheets with first-class support for multiple spreadsheet connections.

Requirements

  • PHP 8.1+
  • Laravel 10, 11, 12, or 13
  • A Google Cloud project with the Sheets API enabled
  • A service account JSON credentials file

Installation

composer require olamilekan/laravel-google-sheets

Publish the configuration file:

php artisan vendor:publish --tag=google-sheets-config

Configuration

1. Credentials

Place your service account JSON file somewhere secure (e.g. storage/app/google/service-account.json) and set the path in your .env:

GOOGLE_SHEETS_CREDENTIALS_PATH=/path/to/service-account.json

2. Spreadsheet Connections

Define as many named connections as you need in config/google-sheets.php:

'sheets' => [

    'default' => [
        'spreadsheet_id' => env('GOOGLE_SHEETS_SPREADSHEET_ID'),
        'sheet' => 'Sheet1',
    ],

    'users' => [
        'spreadsheet_id' => env('GOOGLE_SHEETS_USERS_SPREADSHEET_ID'),
        'sheet' => 'Users',
    ],

    'reports' => [
        'spreadsheet_id' => env('GOOGLE_SHEETS_REPORTS_SPREADSHEET_ID'),
        'sheet' => 'Monthly',
    ],

],

Set the default connection:

GOOGLE_SHEETS_DEFAULT_CONNECTION=default
GOOGLE_SHEETS_SPREADSHEET_ID=your-spreadsheet-id-here

Usage

Using the Facade

use Olamilekan\GoogleSheets\Facades\GoogleSheets;

Reading Data

// All rows from the default connection (first row treated as headers)
$rows = GoogleSheets::all();

// Specific range
$rows = GoogleSheets::range('A1:D10')->get();

// First row only
$row = GoogleSheets::first();

// Get column headers
$headers = GoogleSheets::headers();

// Without header mapping (raw arrays)
$rows = GoogleSheets::connection('users')->withoutHeaders()->get();

Querying Data

// Find rows where a column matches a value
$admins = GoogleSheets::find('role', 'admin');

// Where clause with operators
$highScores = GoogleSheets::where('score', '>=', 90);

// Partial text matching
$results = GoogleSheets::where('name', 'like', 'john');

Writing Data

// Append rows
GoogleSheets::append([
    ['Alice', 'alice@example.com', 'admin'],
    ['Bob', 'bob@example.com', 'user'],
]);

// Update a specific range
GoogleSheets::range('A2:C2')->update([
    ['Alice Updated', 'alice-new@example.com', 'superadmin'],
]);

// Batch update multiple ranges at once
GoogleSheets::batchUpdate([
    'A2:C2' => [['Alice', 'alice@example.com', 'admin']],
    'A3:C3' => [['Bob', 'bob@example.com', 'user']],
]);

// Clear a range
GoogleSheets::range('A2:C100')->clear();

Associative Rows, Upserts, And Validation

// Map associative arrays to the sheet's header row before appending
GoogleSheets::connection('users')->appendAssoc([
    ['name' => 'Alice', 'email' => 'alice@example.com', 'role' => 'admin'],
]);

// Update existing rows by key column and append missing rows
GoogleSheets::connection('users')->upsert('email', [
    ['name' => 'Alice Updated', 'email' => 'alice@example.com', 'role' => 'owner'],
    ['name' => 'Charlie', 'email' => 'charlie@example.com', 'role' => 'user'],
]);

// Validate imported rows with Laravel validation rules
$validRows = GoogleSheets::connection('users')->validate([
    'name' => ['required', 'string'],
    'email' => ['required', 'email'],
]);

// Write row-level validation errors to an "Import Errors" tab before failing
$validRows = GoogleSheets::connection('users')->validateWithErrorSheet([
    'name' => ['required', 'string'],
    'email' => ['required', 'email'],
]);

// Ensure required sheet headers exist
GoogleSheets::connection('users')->requireHeaders(['name', 'email', 'role']);

Import Diff Preview

Preview the impact of an import before writing anything. The preview separates new, changed, deleted, invalid, and conflict rows.

use App\Models\User;
use Olamilekan\GoogleSheets\Facades\GoogleSheets;

$preview = GoogleSheets::connection('users')
    ->diffAgainst(User::query(), key: 'email')
    ->rules([
        'name' => ['required', 'string'],
        'email' => ['required', 'email'],
    ])
    ->preview();

$preview->counts();
// ['new' => 1, 'changed' => 2, 'deleted' => 0, 'invalid' => 1, 'conflicts' => 0]

$preview->new;       // rows in the sheet that are not in the query
$preview->changed;   // rows where sheet values differ from existing query values
$preview->deleted;   // query rows missing from the sheet
$preview->invalid;   // rows failing validation or missing the key
$preview->conflicts; // duplicate key rows in the sheet or query

By default, changed rows compare sheet columns that also exist on the query/model row, excluding the key. You may narrow the comparison:

$preview = GoogleSheets::connection('users')
    ->diffAgainst(User::query(), key: 'email')
    ->only(['name', 'role'])
    ->except(['updated_at'])
    ->preview();

Sync Methods

Sync methods return a SyncReport with created, updated, deleted, skipped, conflict, and failed counts.

use App\Models\User;
use Olamilekan\GoogleSheets\Facades\GoogleSheets;

// Database / Eloquent -> Google Sheet
$report = GoogleSheets::connection('users')
    ->syncFromModel(User::class, keyColumn: 'email', options: [
        'columns' => ['name', 'email', 'role'],
        'conflict' => 'app_wins',
    ]);

// Google Sheet -> Database / Eloquent
$report = GoogleSheets::connection('users')
    ->syncToModel(User::class, keyColumn: 'email');

// CSV -> Google Sheet
$report = GoogleSheets::connection('users')
    ->importCsv(storage_path('app/users.csv'), keyColumn: 'email');

// Google Sheet -> CSV
$report = GoogleSheets::connection('users')
    ->exportCsv(storage_path('app/users-export.csv'));

// API -> Google Sheet
$report = GoogleSheets::connection('orders')
    ->syncFromApi('https://api.example.com/orders', keyColumn: 'order_id', options: [
        'data_key' => 'data',
        'headers' => ['Authorization' => 'Bearer '.$token],
    ]);

// Google Sheet -> API
$report = GoogleSheets::connection('orders')
    ->syncToApi('https://api.example.com/orders/bulk');

// Conflict-aware two-way sync
$report = GoogleSheets::connection('users')
    ->syncTwoWay(User::class, keyColumn: 'email', options: [
        'conflict' => 'fail', // app_wins, sheet_wins, skip, fail
    ]);

$report->counts();
$report->created();
$report->updated();
$report->conflicts();
$report->errors();

Run syncs in the queue:

GoogleSheets::connection('users')
    ->queueSync('syncFromModel', [User::class, 'email'], queue: 'imports');

Every sync is audit logged through Laravel's logger and kept in the in-process audit log:

$records = GoogleSheets::connection('users')->syncAuditLog();

Notify teams when a sync finishes or fails:

GoogleSheets::connection('users')->syncFromModel(User::class, 'email', [
    'notify' => [
        'slack_webhook' => config('services.slack.sync_webhook'),
        'mail_to' => 'ops@example.com',
    ],
]);

Import And Export Classes

use App\Models\User;
use Olamilekan\GoogleSheets\Imports\SheetImport;

class UsersImport extends SheetImport
{
    public function target()
    {
        return User::query();
    }

    public function key(): string
    {
        return 'email';
    }
    public ?string $errorSheet = 'Import Errors';

    public function rules(): array
    {
        return ['email' => ['required', 'email']];
    }

    public function model(array $row): User
    {
        return User::updateOrCreate(
            ['email' => $row['email']],
            ['name' => $row['name']]
        );
    }
}

GoogleSheets::import(new UsersImport(), 'users');

Preview an import from the command line without writing rows:

php artisan google-sheets:sync "App\\Imports\\UsersImport" users --dry-run

Dry-run imports compare the sheet rows against the import class target() using the column returned by key(). Validation rules are applied when the import defines rules().

use App\Models\Report;
use Olamilekan\GoogleSheets\Exports\SheetExport;

class ReportsExport extends SheetExport
{
    public bool $replace = true;

    public function headings(): array
    {
        return ['Date', 'Name', 'Total'];
    }

    public function collection()
    {
        return Report::query()
            ->latest()
            ->get()
            ->map(fn (Report $report) => [
                $report->created_at->toDateString(),
                $report->name,
                $report->total,
            ]);
    }
}

GoogleSheets::export(new ReportsExport(), 'reports');

Multiple Connections

// Switch between configured connections
$users  = GoogleSheets::connection('users')->all();
$reports = GoogleSheets::connection('reports')->all();

// Create an ad-hoc connection to any spreadsheet
$data = GoogleSheets::make('some-spreadsheet-id', 'TabName')->all();

Switching Sheets (Tabs) at Runtime

$sheet = GoogleSheets::connection('default');

$sheet1Data = $sheet->sheet('Sheet1')->all();
$sheet2Data = $sheet->sheet('Sheet2')->all();

Sheet / Tab Management

// List all sheet tabs in a spreadsheet
$tabs = GoogleSheets::listSheets();   // ['Sheet1', 'Users', 'Reports']

// Check if a tab exists
GoogleSheets::sheetExists('Users');   // true

// Create a new tab
GoogleSheets::createSheet('Archive');

// Duplicate an existing tab
GoogleSheets::duplicateSheet('Sheet1', 'Sheet1 Copy');

// Delete a tab
GoogleSheets::deleteSheet('Archive');

Caching

Enable caching in config or at runtime to reduce API calls:

// In config/google-sheets.php
'cache' => [
    'enabled' => true,
    'store'   => 'redis',
    'ttl'     => 300,  // seconds
    'prefix'  => 'google_sheets_',
],

// At runtime
$rows = GoogleSheets::enableCache(600)->all();
$rows = GoogleSheets::disableCache()->all();

Write operations now clear remembered read cache keys for the active spreadsheet, so cached ranges are refreshed after updates.

Retry And Backoff

Transient Google Sheets API failures are retried by default, including rate limits, quota throttling, and backend errors. The delay uses exponential backoff with jitter.

// In config/google-sheets.php
'retry' => [
    'enabled' => true,
    'attempts' => 3,
    'delay' => 250,      // milliseconds
    'max_delay' => 5000, // milliseconds
],

// At runtime
$rows = GoogleSheets::withRetries(attempts: 5, delay: 500)->all();
$rows = GoogleSheets::withoutRetries()->all();

Chunked Processing

GoogleSheets::chunk(100, function ($chunk) {
    foreach ($chunk as $row) {
        // process each row
    }
});

GoogleSheets::lazy(500)->each(function (array $row) {
    // process one row at a time
});

Formatting, Formulas, And Named Ranges

GoogleSheets::connection('reports')
    ->sheet('Monthly')
    ->boldHeader()
    ->freezeRows(1)
    ->autoResizeColumns(1, 4);

GoogleSheets::connection('reports')->append([
    ['Total', GoogleSheets::formula('SUM(C2:C100)')],
]);

$summaryRows = GoogleSheets::connection('reports')
    ->namedRange('MonthlySummary')
    ->get();

Testing

use Olamilekan\GoogleSheets\Facades\GoogleSheets;

$fake = GoogleSheets::fake([
    'users' => [
        ['name' => 'Alice', 'email' => 'alice@example.com'],
    ],
]);

GoogleSheets::connection('users')->appendAssoc([
    ['name' => 'Bob', 'email' => 'bob@example.com'],
]);

$fake->assertAppended('users', ['name' => 'Bob', 'email' => 'bob@example.com']);

Artisan Commands

php artisan google-sheets:list users
php artisan google-sheets:clear reports --sheet=Monthly --range=A2:D100
php artisan google-sheets:sync "App\\Imports\\UsersImport" users
php artisan google-sheets:sync "App\\Imports\\UsersImport" users --dry-run
php artisan google-sheets:sync "App\\Exports\\ReportsExport" reports

Spreadsheet Metadata

$title = GoogleSheets::getTitle();
$id    = GoogleSheets::getSpreadsheetId();

Dependency Injection

use Olamilekan\GoogleSheets\GoogleSheetsManager;

class UserImportService
{
    public function __construct(
        protected GoogleSheetsManager $sheets
    ) {}

    public function import(): void
    {
        $rows = $this->sheets->connection('users')->all();

        foreach ($rows as $row) {
            User::updateOrCreate(
                ['email' => $row['email']],
                ['name' => $row['name']]
            );
        }
    }
}

API Reference

GoogleSheetsManager

Method Description
connection(?string $name) Get a named connection (lazy-loaded & cached)
make(string $spreadsheetId, string $sheet) Create an ad-hoc sheet instance
getDefaultConnection() Get the default connection name
purge(?string $name) Remove a resolved connection
reconnect(?string $name) Purge and re-resolve a connection

Sheet

Method Returns Description
spreadsheet(string $id) static Override the spreadsheet ID
sheet(string $name) static Switch to a different tab
range(string $range) static Set A1 range for the next operation
get() Collection Read rows (headers mapped)
all() Collection Read all rows from the sheet
first() ?array First data row
last() ?array Last data row
headers() array Column headers (row 1)
find(col, val) Collection Filter rows by column value
where(col, op, val) Collection Filter with comparison operators
chunk(size, cb) void Process rows in chunks
append(array $rows) int Append rows (returns row count)
update(array $rows) int Update range (returns row count)
batchUpdate(array $data) int Update multiple ranges
clear() bool Clear values in range
appendAssoc(array) int Append associative rows mapped to sheet headers
updateAssoc(array) int Update associative rows mapped to sheet headers
upsert(key, rows) int Update rows by key column and append missing rows
syncRows(rows, key, options) SyncReport Sync array/collection rows into the sheet
syncFromModel(model, key, options) SyncReport Sync Eloquent model records into the sheet
syncToModel(model, key, options) SyncReport Sync sheet rows into an Eloquent model
importCsv(path, key, options) SyncReport Sync a CSV file into the sheet
exportCsv(path, options) SyncReport Export sheet rows to a CSV file
syncFromApi(url, key, options) SyncReport Pull JSON rows from an API into the sheet
syncToApi(url, options) SyncReport Push sheet rows to an API endpoint
syncTwoWay(target, key, options) SyncReport Run conflict-aware two-way sync
queueSync(method, args, queue) PendingDispatch Dispatch a sync method to Laravel's queue
syncAuditLog() Collection Read in-process sync audit records
validate(rules) Collection Validate rows with Laravel validation rules
requireHeaders(array) static Ensure required headers exist
lazy(size) LazyCollection Iterate rows lazily from a collection-backed read
createSheet(string) static Add a new tab
deleteSheet(string) bool Remove a tab
duplicateSheet(src, new) static Copy a tab
listSheets() array List all tab names
sheetExists(string) bool Check if a tab exists
namedRange(string) static Set a named range for the next operation
listNamedRanges() array List named ranges
formula(string) string Create a formula cell value
boldHeader() static Bold the first row
freezeRows(int) static Freeze leading rows
autoResizeColumns(start, end) static Auto-resize columns
formatRange(range, format) static Apply cell formatting
withHeaders() static Map first row as keys (default)
withoutHeaders() static Return raw arrays
enableCache(?int $ttl) static Enable caching
disableCache() static Disable caching
withRetries(?attempts, ?delay) static Enable retries and optionally override attempts/delay
withoutRetries() static Disable retries for the current sheet instance

Environment Variables

Variable Default Description
GOOGLE_SHEETS_CREDENTIALS_PATH storage/app/google/service-account.json Path to credentials
GOOGLE_SHEETS_DEFAULT_CONNECTION default Default connection name
GOOGLE_SHEETS_SPREADSHEET_ID Spreadsheet ID for default connection
GOOGLE_SHEETS_APPLICATION_NAME Laravel Google Sheets App name for API requests
GOOGLE_SHEETS_CACHE_ENABLED false Enable response caching
GOOGLE_SHEETS_CACHE_STORE null (default driver) Cache store to use
GOOGLE_SHEETS_CACHE_TTL 300 Cache lifetime in seconds
GOOGLE_SHEETS_RETRY_ENABLED true Retry transient Google API failures
GOOGLE_SHEETS_RETRY_ATTEMPTS 3 Maximum attempts per API call
GOOGLE_SHEETS_RETRY_DELAY 250 Initial retry delay in milliseconds
GOOGLE_SHEETS_RETRY_MAX_DELAY 5000 Maximum retry delay in milliseconds
GOOGLE_SHEETS_VALUE_RENDER FORMATTED_VALUE Value render option
GOOGLE_SHEETS_VALUE_INPUT USER_ENTERED Value input option

License

MIT

olamilekan/laravel-google-sheets 适用场景与选型建议

olamilekan/laravel-google-sheets 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 9 次下载、GitHub Stars 达 5, 最近一次更新时间为 2026 年 03 月 30 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「spreadsheet」 「laravel」 「google-api」 「google-sheets」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 olamilekan/laravel-google-sheets 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 olamilekan/laravel-google-sheets 我们能提供哪些服务?
定制开发 / 二次开发

基于 olamilekan/laravel-google-sheets 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 9
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 5
  • 点击次数: 31
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 5
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-03-30