Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Scopus Laravel Laravel Package

mgknetcom/scopus-laravel

A Laravel package for integrating Elsevier's Scopus API, offering simple access to scholarly publication, author, and citation data so you can query Scopus directly from your Laravel applications.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the first stable release, so there's no legacy surface to work around — but it also means the API is fresh and documentation may lag behind the code. Start by reading the package source rather than guessing at conventions.

Prerequisites

  • PHP 8.3+, Laravel 12 or 13
  • A Scopus/Elsevier API key (developer portal). If you're on an institutional subscription you'll also need the institutional token — the package expects both, and a key without the token silently returns reduced metadata in some endpoints.
  • A queue worker (Redis or database) — syncing publications for even a handful of authors is not a request-cycle operation.

Install and wire up

composer require <vendor>/<package>
php artisan vendor:publish --tag="<package>-config"
php artisan vendor:publish --tag="<package>-migrations"
php artisan migrate

Put credentials in .env and reference them only from config/:

SCOPUS_API_KEY=
SCOPUS_INST_TOKEN=
SCOPUS_THROTTLE=2

Where to look first

  1. config/<package>.php — throttle settings, model bindings (which of your models represent an author), queue name, and the toggle for the Filament integration.
  2. The package service provider — this is where the HTTP client, the author matcher, and the pending-suggestion handler are bound. Those bindings are your extension points.
  3. The migrations — check whether the package owns your users table relationship or expects a scopus_author_id column you add yourself. Add it if needed.
  4. Filament plugin registration — optional, and only if filament/filament:^5.0 is actually installed.

First use case

Link a single known author and pull their profile, metrics, and publications:

use Vendor\Scopus\Facades\Scopus;

$author = Scopus::authors()->find('7005123456'); // Scopus author ID
$author->metrics();       // h-index, citation counts, document counts
$author->publications();  // paginated document records

Do this in php artisan tinker first. Once the payload shape looks right, move it into a queued job and attach it to your User model via a scopus_author_id column. That single-author loop is 90% of what most installs need; the pending-authorship workflow is the layer you add once you have more authors than you can eyeball.

Implementation Patterns

Author linking is your problem, not the package's. The package resolves Scopus records; it cannot know which of your users is which researcher. The reliable pattern is an explicit scopus_author_id on your user/author model, seeded from an admin UI or an import, plus a nullable second ID to accommodate researchers with duplicate Scopus profiles (this happens more than you'd expect after name changes).

Schema::table('users', function (Blueprint $table) {
    $table->string('scopus_author_id')->nullable()->unique();
    $table->timestamp('scopus_synced_at')->nullable();
});

Wrap every sync in a job with rate limiting. Scopus quotas are the single biggest operational constraint. Use Laravel's RateLimited middleware with a named limiter rather than sleep() calls:

// AppServiceProvider::boot()
RateLimiter::for('scopus', fn () => Limit::perSecond(
    config('scopus.throttle', 2)
));
class SyncAuthorFromScopus implements ShouldQueue
{
    use InteractsWithQueue, Queueable;

    public int $timeout = 120;
    public int $tries = 3;
    public array $backoff = [30, 120, 600];

    public function middleware(): array
    {
        return [new RateLimited('scopus')];
    }
}

Two-tier sync. Profile + metrics are cheap (one or two calls); publication discovery is expensive and paginated. Split them into SyncAuthorProfile and SyncAuthorPublications jobs so a quota exhaustion on publications doesn't block metrics refreshes. Schedule profile syncs nightly and publication discovery weekly.

Upsert publications by EID, not by title. EIDs are stable; titles are not, and Scopus normalises punctuation inconsistently across records. Keep a doi column too and fall back to it for cross-referencing, but treat EID as the primary key for idempotency.

Publication::upsert(
    $records->map(fn ($r) => [
        'eid' => $r->eid,
        'doi' => $r->doi,
        'title' => $r->title,
        'cited_by' => $r->citedByCount,
        'synced_at' => now(),
    ])->all(),
    ['eid'],
    ['doi', 'title', 'cited_by', 'synced_at']
);

Store metric snapshots, not just current values. Citation counts and h-index are point-in-time. If you want trend lines or "citations this quarter" widgets, write a row per sync into a author_metric_snapshots table. Overwriting a single row loses the history permanently and there's no way to backfill it from the API.

Pending-authorship workflow. The intended flow is: the package surfaces candidate matches (publications that reference a name variant not yet linked to a known author), you review and approve/reject, and approval attaches the publication to the right local author. Two patterns work:

  • Filament-native — register the package's resource in your panel provider and gate it with a policy. Fastest path; use this if you already run Filament 5.
  • Custom UI — listen for the suggestion-created event, route a notification to a moderator, and resolve via the package's suggestion service. Use this if your admin lives in Nova, a custom Blade dashboard, or you want Slack approval flows.

Either way, record who approved and when. You will be asked "why is this paper on my profile" and a bare boolean won't answer it.

Filament 5 is genuinely optional. Keep it out of your non-Filament code paths. If you need conditional behaviour, check class_exists(\Filament\Panel::class) rather than letting the package's service provider auto-register resources into a panel that may not exist.

Testing. The package's HTTP layer goes through Laravel's HTTP client, so Http::fake() with recorded Scopus fixtures works cleanly. Record real responses once (mind the API terms) and replay them — hand-writing Scopus JSON fixtures is a reliable source of false-green tests, because the response shapes for author search vs. author retrieval vs. abstract retrieval all differ.

Gotchas and Tips

Quotas are the failure mode, and they fail quietly. Scopus returns 429 when throttled but can also return 200 with an empty result set when a quota window is exhausted on some endpoints. Always log the raw response and check for empty-vs-error ambiguity. Distinguish in your logs: 401 = bad or missing key, 403 = key valid but not entitled to that API (institutional vs. free tier differences are real), 429 = throttle/quota, 404 = unknown author ID.

An empty author record is not an error. New researchers, or researchers who publish under a different name, frequently have an author ID with zero documents. Your UI needs a genuine "no data yet" state distinct from "sync failed", or support tickets will be unresolvable.

Scopus author IDs are not one-per-person. Expect duplicates, and expect researchers to publish under multiple profiles. Design your linking as many-to-one (several Scopus IDs → one local user) from the start; retrofitting that after you've made scopus_author_id unique-per-user is painful.

Citation counts lag by weeks. If you surface "citations" next to a publication, show the synced_at timestamp. Otherwise users will compare against scopus.com, see a mismatch, and file a bug.

Config caching. In production, php artisan config:cache means env() calls outside config/ return null. If credentials read as empty after deploy, that's why — re-cache after changing .env. Similarly, if you publish the config after caching, changes won't take effect until you re-run it.

Queue timeouts on large authors. A prolific author with 400 publications means many paginated calls. chunkById your local author list and dispatch one job per author rather than looping inside a single job; a worker timeout at publication 250 loses all partial progress unless each publication is upserted as you go (do that — upsert per page, not at the end).

Filament upgrade surface. Filament 5 changed resource and form APIs relative to v3/v4. If you're coming from an older Filament, verify the package's admin resource actually renders — a version constraint in composer.json can be satisfied while the resource's table columns use a removed method. Do a smoke test of the panel after any Filament minor bump.

Migration collisions. Publishing migrations into an app that already has publications or authors tables will either fail or, worse, succeed and conflict semantically. Read the published migration files before running migrate on an existing database.

Extension points worth knowing about. The author matcher (name normalisation / fuzzy matching for suggestions) and the HTTP client binding are the two you'll most likely need to override. Bind your own implementation in a service provider rather than forking — this package is at 1.0.0, so expect churn in 1.x and keep your diff small. Publish and customise views rather than editing vendor files.

Rate-limit your own retries. tries = 3 with backoff on a 429 will amplify a quota problem into a longer outage. Inspect the exception type in failed() and only retry throttles, never 401/403 — those need a human to fix credentials.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core