**
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
- Set up OJS 3.5.0-5 on PostgreSQL with at least one published article.
- Request:
https://<host>/<journal>/oai?verb=ListRecords&metadataPrefix=oai_dc&from=YYYY-MM-DD&until=YYYY-MM-DD - 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)