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

Oai Pmh Laravel Package

ottosmops/oai-pmh

Laravel/PHP package that adds an OAI-PMH 2.0 data-provider endpoint to Laravel applications, making repository and digital-library records harvestable through standard verbs like Identify, ListMetadataFormats, ListRecords, and GetRecord, with configurable metadata formats and sets.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Confirm platform compatibility before installing: v1.0.0 supports PHP 8.2 / 8.3 / 8.4 and Laravel 10 / 11 / 12.
    composer require ottosmops/oai-pmh
    
  2. Publish the config and, if needed, the Blade views (7 views ship with the package):
    php artisan vendor:publish --tag=oai-pmh-config
    php artisan vendor:publish --tag=oai-pmh-views
    
    Check the README for the exact publish tags if your install does not expose them.
  3. Open config/oai-pmh.php first. Set the repository identity values (base_url, repositoryName, adminEmail), enabled metadata formats, set definitions, deleted-record mode, and date granularity.
  4. Implement OaiRepositoryContract and bind it in a service provider. The contract is deliberately small — three methods: get, getRecordForId, and getEarliestDatestamp. Start by wrapping an existing Eloquent model that represents published archive records.
  5. Hit the configured OAI-PMH endpoint and validate Identify, ListMetadataFormats, and a small ListRecords request before wiring up sets or custom XML.

The first practical use case is exposing an existing Laravel model as oai_dc records so an external harvester can discover and harvest the collection without building the OAI-PMH protocol layer yourself. The package is battle-tested: it was extracted from the Anton archive-management system (built with the Gosteli Archive), so the protocol edge cases around sets, deletions, and resumption are already handled.

Implementation Patterns

  • Repository adapter around Eloquent. Create a dedicated class such as EloquentOaiRepository that implements OaiRepositoryContract. Implement the three methods narrowly:

    • get — handle the query parameters (metadata prefix, datestamp range, set, pagination window) and return a page of records.
    • getRecordForId — resolve a single record header plus metadata, or signal "not found" so the package can emit the correct OAI-PMH error.
    • getEarliestDatestamp — return the oldest datestamp in the repository, used by Identify.

    Return records in the exact shape expected by the published views/contract, and keep ordering deterministic — usually datestamp plus identifier — so resumption tokens remain stable.

  • Keep protocol logic out of controllers. Bind the contract in AppServiceProvider and let the package route/controller call it. Your application code should only translate OAI-PMH filters into database queries and map models into OAI record headers and metadata payloads.

  • Metadata formats as views/transformers. For oai_dc, either return an array of Dublin Core fields expected by the default view or override the Blade view to render your model directly. If you support additional prefixes, add them in config and provide the corresponding XML view/transform. Validate each format against the OAI-PMH XSD.

  • Sets as query scopes. Map configured set specs to application scopes or relationships. Because the package supports set hierarchy with descendant inclusion, a parent set query should include records from child sets. Test that ListSets, ListIdentifiers?set=..., and ListRecords?set=... agree.

  • Deleted-record mode drives repository behaviour. Decide early whether you use no, persistent, or transient. If you return deleted records, include the deleted status in the record header and omit metadata. Ensure deleting or unpublishing a record updates its datestamp so harvesters see the change.

  • Resumption tokens need efficient pagination. Do not load the full collection in get. Use offsets, cursors, or chunked queries that match the contract's pagination parameters. Keep cursor and completeListSize accurate; harvesters rely on them for progress and retry behaviour.

  • Normalise dates at the boundary. Store/query in UTC and return datestamps in the granularity configured for the repository. If you use day granularity, make sure from/until semantics include the expected day boundaries; if you use second granularity, return full ISO-8601 UTC timestamps.

  • Cache stable responses. Identify, ListMetadataFormats, and ListSets rarely change but can be requested frequently by harvesters. Cache them at the application layer if your dataset is large or your endpoint is public.

  • Test with real OAI-PMH requests. Exercise all six verbs, each of the eight error codes (bad verb, bad argument, cannot dissect, bad resumption token, cannot open/resume, no records match, etc.), an invalid metadata prefix, an unknown identifier, an empty result set, and a multi-page ListRecords harvest. The package's XSD-conformance suite is useful, but also test your repository mapping with production-like data.

Gotchas and Tips

  • Identifiers must be stable. Do not expose auto-increment IDs if they can change or be reused. Use a persistent slug, ARK, DOI, or UUID. Harvesters cache identifiers, so changing them looks like records disappearing and new records appearing.
  • Datestamps are easy to get wrong. OAI-PMH requires UTC datestamps and forbids future dates. Update the datestamp whenever metadata, sets, or deletion status changes. If getEarliestDatestamp() is wrong, Identify will be wrong and some harvesters will behave unpredictably.
  • Deleted records need a datestamp too. In persistent mode, deleted records remain visible indefinitely; in transient mode they may disappear. Make sure your repository returns deleted headers correctly and does not accidentally include metadata for them.
  • Resumption tokens are opaque to harvesters. Do not rely on session state, and do not change the result set between pages. If you customise token handling, sign or cache tokens and ensure the original query arguments are preserved. A token that expires too quickly or returns inconsistent completeListSize will break large harvests.
  • XML escaping is non-negotiable. Blade views are convenient, but metadata values must be escaped correctly. Avoid raw unescaped model output, and be careful with CDATA. Validate responses against the OAI-PMH XSD after customising views.
  • Config cache can hide changes. After editing config/oai-pmh.php, run php artisan config:clear in development. In production, deploy with config:cache and verify the published views are present.
  • Set hierarchy affects queries. Descendant inclusion means a parent set must include child-set records. If your set mapping only checks an exact set column, parent-set harvests will silently miss records.
  • Unsupported metadata prefixes must fail cleanly. ListMetadataFormats should advertise only formats you actually render. If a harvester requests an unsupported prefix, let the package return the correct OAI-PMH error rather than throwing a raw exception.
  • Do not bypass the contract. The single three-method extension point is intentional. If you need custom XML, override views; if you need custom filtering, implement it inside the repository. Avoid patching vendor code or reaching into package internals, or upgrades will become painful.
  • Upgrading within v1.x. As the first stable release, the protocol surface and contract signature are the compatibility promise. Pin to ^1.0, and check the changelog for contract changes before assuming a minor bump is a drop-in.
  • Watch large-collection performance. Harvesters can issue many sequential requests. Avoid N+1 queries when loading metadata and sets, add indexes on datestamp/set/identifier columns, and consider caching Identify/ListSets if the endpoint is public.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource