Skip to content

Commit b73bf3a

Browse files
feat: make DOI optional for manual entry in citation form (#1373)
* feat: make DOI optional in manual citation entry. * chore: update composer.lock * chore: add missing test and copilot review changes
1 parent ad1b83d commit b73bf3a

7 files changed

Lines changed: 3080 additions & 2735 deletions

File tree

app/Actions/Citation/SyncCitations.php

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,9 @@ public function sync(Project $project, array $citations, User $user): array
3535
foreach ($citations as $citationData) {
3636
$this->validateCitationData($citationData);
3737

38-
$doi = $citationData['doi'];
39-
40-
if (! is_null($doi)) {
41-
$citation = $this->findOrCreateCitation($project, $citationData, $doi);
42-
$processedCitations[] = $citation;
43-
}
38+
$citation = $this->findOrCreateCitation($project, $citationData);
39+
$this->rememberCitation($project, $citation);
40+
$processedCitations[] = $citation;
4441
}
4542

4643
DB::transaction(function () use ($project, $processedCitations, $user): void {
@@ -61,7 +58,7 @@ private function validateCitationData(array $citationData): void
6158
{
6259
Validator::make($citationData, [
6360
'title' => ['required', 'string'],
64-
'doi' => ['required', 'string'],
61+
'doi' => ['nullable', 'string'],
6562
'authors' => ['required', 'string'],
6663
'citation_text' => ['nullable', 'string'],
6764
])->validate();
@@ -72,11 +69,31 @@ private function validateCitationData(array $citationData): void
7269
*
7370
* @param array<string, mixed> $citationData
7471
*/
75-
private function findOrCreateCitation(Project $project, array $citationData, string $doi): Citation
72+
private function findOrCreateCitation(Project $project, array $citationData): Citation
7673
{
77-
$existingCitation = $project->citations->filter(function ($citation) use ($doi) {
78-
return $doi === $citation->doi;
79-
})->first();
74+
$doi = $this->normalizeDoi($citationData['doi'] ?? null);
75+
$title = $this->normalizeText($citationData['title'] ?? null);
76+
$authors = $this->normalizeText($citationData['authors'] ?? null);
77+
78+
$existingCitation = null;
79+
80+
// 1. Try to match by ID first (explicit reference)
81+
if (! empty($citationData['id'])) {
82+
$existingCitation = $project->citations->firstWhere('id', (int) $citationData['id']);
83+
}
84+
85+
// 2. Try to match by DOI (if not null/empty)
86+
if (! $existingCitation && ! is_null($doi)) {
87+
$existingCitation = $project->citations->firstWhere('doi', $doi);
88+
}
89+
90+
// 3. Try to match by title + authors (content-based matching for missing DOI)
91+
if (! $existingCitation && ! is_null($title) && ! is_null($authors)) {
92+
$existingCitation = $project->citations->first(function ($citation) use ($title, $authors): bool {
93+
return $this->normalizeText($citation->title) === $title
94+
&& $this->normalizeText($citation->authors) === $authors;
95+
});
96+
}
8097

8198
if ($existingCitation) {
8299
$existingCitation->update($this->prepareCitationAttributes($citationData));
@@ -96,10 +113,49 @@ private function findOrCreateCitation(Project $project, array $citationData, str
96113
private function prepareCitationAttributes(array $citationData): array
97114
{
98115
return [
99-
'doi' => $citationData['doi'] ?? null,
100-
'title' => $citationData['title'] ?? null,
101-
'authors' => $citationData['authors'] ?? null,
102-
'citation_text' => $citationData['citation_text'] ?? null,
116+
'doi' => $this->normalizeDoi($citationData['doi'] ?? null),
117+
'title' => $this->normalizeText($citationData['title'] ?? null),
118+
'authors' => $this->normalizeText($citationData['authors'] ?? null),
119+
'citation_text' => $this->normalizeText($citationData['citation_text'] ?? null),
103120
];
104121
}
122+
123+
private function rememberCitation(Project $project, Citation $citation): void
124+
{
125+
$citations = $project->citations;
126+
127+
if (! $citations->contains('id', $citation->id)) {
128+
$project->setRelation('citations', $citations->push($citation));
129+
}
130+
}
131+
132+
private function normalizeDoi(mixed $doi): ?string
133+
{
134+
if (! is_string($doi)) {
135+
return null;
136+
}
137+
138+
$normalizedDoi = trim($doi);
139+
140+
if ($normalizedDoi === '') {
141+
return null;
142+
}
143+
144+
return $normalizedDoi;
145+
}
146+
147+
private function normalizeText(mixed $value): ?string
148+
{
149+
if (! is_string($value)) {
150+
return null;
151+
}
152+
153+
$normalizedValue = trim($value);
154+
155+
if ($normalizedValue === '') {
156+
return null;
157+
}
158+
159+
return $normalizedValue;
160+
}
105161
}

app/Models/Validation.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,42 @@ public function process()
242242
array_push($studiesValidation, $studyReport);
243243
}
244244

245+
// Validate citations
246+
$citations = $project->citations;
247+
$citationsValidation = [];
248+
$citationsStatus = true;
249+
250+
foreach ($citations as $citation) {
251+
$citationReport = [
252+
'name' => $citation->title ?? 'Untitled',
253+
'id' => $citation->id,
254+
];
255+
256+
// Check if DOI is present
257+
$hasDoi = is_string($citation->doi) && trim($citation->doi) !== '';
258+
259+
if ($hasDoi) {
260+
$citationReport['doi'] = 'true|required';
261+
} else {
262+
$citationReport['doi'] = 'false|required';
263+
$citationsStatus = false; // Citation validation failed
264+
}
265+
266+
$citationReport['status'] = $hasDoi;
267+
array_push($citationsValidation, $citationReport);
268+
}
269+
270+
// Set overall citations validation status and store detailed report
271+
if ($citationsStatus) {
272+
$report['project']['citations'] = 'true|required';
273+
} else {
274+
$report['project']['citations'] = 'false|required';
275+
$status = false; // Propagate to project status
276+
}
277+
278+
// Store detailed citations validation data separately
279+
$report['project']['citations_detail'] = $citationsValidation;
280+
245281
$report['project']['studies'] = $studiesValidation;
246282
$report['project']['status'] = $status;
247283
$project->validation_status = $status;

0 commit comments

Comments
 (0)