TypeError in OAIDAO when filtering OAI ListRecords by date (from/until) on PostgreSQL

**
Environment:**

  • OJS version: 3.5.0-5
  • Database: PostgreSQL
  • PHP: 8.3

Describe the issue

Calling the OAI-PMH ListRecords verb with from and/or until parameters throws an uncaught TypeError and returns a fatal error instead of the expected XML response. This affects any harvester (e.g. DOAJ, institutional repositories) that queries OAI with a date range, which is standard practice for incremental harvesting.

Steps to reproduce

  1. Set up OJS 3.5.0-5 on PostgreSQL with at least one published article.
  2. Request:
    https://<host>/<journal>/oai?verb=ListRecords&metadataPrefix=oai_dc&from=YYYY-MM-DD&until=YYYY-MM-DD
  3. Observe the fatal error in the PHP error log.

Error log

PHP Fatal error:  Uncaught TypeError: str_contains(): Argument #1 ($haystack) must be of type string, Illuminate\Database\Query\Expression given in .../lib/pkp/lib/vendor/laravel/framework/src/Illuminate/Database/Grammar.php:187
Stack trace:
...
#19 .../lib/pkp/classes/oai/PKPOAIDAO.php(148): Illuminate\Database\Query\Builder->getCountForPagination()
#20 .../classes/oai/ojs/JournalOAI.php(195): PKP\oai\PKPOAIDAO->getRecords()

Root cause

In classes/oai/ojs/OAIDAO.php, method _getRecordsRecordSetQuery(), the date-range conditions pass a DB::raw() expression as the column argument to Laravel’s whereDate():

php

->when($from, function ($query, $from) {
    return $query->whereDate(DB::raw('GREATEST(a.last_modified, i.last_modified, p.last_modified)'), '>=', \DateTime::createFromFormat('U', $from));
})
->when($until, function ($query, $until) {
    return $query->whereDate(DB::raw('GREATEST(a.last_modified, i.last_modified, p.last_modified)'), '<=', \DateTime::createFromFormat('U', $until));
})

whereDate() internally calls str_contains() on the column argument to check for JSON path syntax, and does not accept an Expression object as a string — hence the TypeError. This appears to be a regression from a Laravel query builder version where Expression objects were implicitly cast to string in this code path.

Suggested fix

Replace whereDate(DB::raw(...), ...) with an explicit whereRaw() comparison, e.g.:

php

->when($from, function ($query, $from) {
    return $query->whereRaw('GREATEST(a.last_modified, i.last_modified, p.last_modified)::date >= ?', [\DateTime::createFromFormat('U', $from)->format('Y-m-d')]);
})
->when($until, function ($query, $until) {
    return $query->whereRaw('GREATEST(a.last_modified, i.last_modified, p.last_modified)::date <= ?', [\DateTime::createFromFormat('U', $until)->format('Y-m-d')]);
})

This has been tested locally and resolves the issue, returning valid OAI-PMH XML with correctly filtered records. Note the ::date cast is PostgreSQL-specific syntax; a cross-database fix may need a driver check or a different casting approach for MySQL.

Impact

Any OJS 3.5.0-5 installation on PostgreSQL is unable to serve incremental OAI-PMH harvests (date-filtered), which breaks integration with DOAJ and other metadata aggregators relying on the from/until parameters.

Note: This report was drafted with the assistance of an AI (Claude)

Hi @Adriano_Jose,

This is an upstream error in 3rd party code; see: whereDate with Postgres does not accept expression for $column parameter · Issue #60539 · laravel/framework · GitHub

Regards,
Alec Smecher
Public Knowledge Project Team