xref: /webtrees/app/Statistics/Repository/FamilyDatesRepository.php (revision cab242e7d7a773b0a6dab130048696e26fd6612c)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2019 webtrees development team
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16declare(strict_types=1);
17
18namespace Fisharebest\Webtrees\Statistics\Repository;
19
20use Fisharebest\Webtrees\Date;
21use Fisharebest\Webtrees\Fact;
22use Fisharebest\Webtrees\Functions\FunctionsPrint;
23use Fisharebest\Webtrees\GedcomRecord;
24use Fisharebest\Webtrees\I18N;
25use Fisharebest\Webtrees\Statistics\Repository\Interfaces\FamilyDatesRepositoryInterface;
26use Fisharebest\Webtrees\Tree;
27use Illuminate\Database\Capsule\Manager as DB;
28use Illuminate\Database\Eloquent\Model;
29use Illuminate\Database\Query\Builder;
30
31/**
32 * A repository providing methods for family dates related statistics (birth, death, marriage, divorce).
33 */
34class FamilyDatesRepository implements FamilyDatesRepositoryInterface
35{
36    /**
37     * Sorting directions.
38     */
39    private const SORT_MIN = 'MIN';
40    private const SORT_MAX = 'MAX';
41
42    /**
43     * Event facts.
44     */
45    private const EVENT_BIRTH    = 'BIRT';
46    private const EVENT_DEATH    = 'DEAT';
47    private const EVENT_MARRIAGE = 'MARR';
48    private const EVENT_DIVORCE  = 'DIV';
49
50    /**
51     * @var Tree
52     */
53    private $tree;
54
55    /**
56     * Constructor.
57     *
58     * @param Tree $tree
59     */
60    public function __construct(Tree $tree)
61    {
62        $this->tree = $tree;
63    }
64
65    /**
66     * Returns the first/last event record for the given event fact.
67     *
68     * @param string $fact
69     * @param string $operation
70     *
71     * @return Model|object|static|null
72     */
73    private function eventQuery(string $fact, string $operation)
74    {
75        return DB::table('dates')
76            ->select(['d_gid as id', 'd_year as year', 'd_fact AS fact', 'd_type AS type'])
77            ->where('d_file', '=', $this->tree->id())
78            ->where('d_fact', '=', $fact)
79            ->where('d_julianday1', '=', function (Builder $query) use ($operation, $fact) {
80                $query->selectRaw($operation . '(d_julianday1)')
81                    ->from('dates')
82                    ->where('d_file', '=', $this->tree->id())
83                    ->where('d_fact', '=', $fact)
84                    ->where('d_julianday1', '<>', 0);
85            })
86            ->first();
87    }
88
89    /**
90     * Returns the formatted year of the first/last occuring event.
91     *
92     * @param string $type      The fact to query
93     * @param string $operation The sorting operation
94     *
95     * @return string
96     */
97    private function getFirstLastEvent(string $type, string $operation): string
98    {
99        $row    = $this->eventQuery($type, $operation);
100        $result = I18N::translate('This information is not available.');
101
102        if ($row) {
103            $record = GedcomRecord::getInstance($row->id, $this->tree);
104
105            if ($record && $record->canShow()) {
106                $result = $record->formatList();
107            } else {
108                $result = I18N::translate('This information is private and cannot be shown.');
109            }
110        }
111
112        return $result;
113    }
114
115    /**
116     * @inheritDoc
117     */
118    public function firstBirth(): string
119    {
120        return $this->getFirstLastEvent(self::EVENT_BIRTH, self::SORT_MIN);
121    }
122
123    /**
124     * @inheritDoc
125     */
126    public function lastBirth(): string
127    {
128        return $this->getFirstLastEvent(self::EVENT_BIRTH, self::SORT_MAX);
129    }
130
131    /**
132     * @inheritDoc
133     */
134    public function firstDeath(): string
135    {
136        return $this->getFirstLastEvent(self::EVENT_DEATH, self::SORT_MIN);
137    }
138
139    /**
140     * @inheritDoc
141     */
142    public function lastDeath(): string
143    {
144        return $this->getFirstLastEvent(self::EVENT_DEATH, self::SORT_MAX);
145    }
146
147    /**
148     * @inheritDoc
149     */
150    public function firstMarriage(): string
151    {
152        return $this->getFirstLastEvent(self::EVENT_MARRIAGE, self::SORT_MIN);
153    }
154
155    /**
156     * @inheritDoc
157     */
158    public function lastMarriage(): string
159    {
160        return $this->getFirstLastEvent(self::EVENT_MARRIAGE, self::SORT_MAX);
161    }
162
163    /**
164     * @inheritDoc
165     */
166    public function firstDivorce(): string
167    {
168        return $this->getFirstLastEvent(self::EVENT_DIVORCE, self::SORT_MIN);
169    }
170
171    /**
172     * @inheritDoc
173     */
174    public function lastDivorce(): string
175    {
176        return $this->getFirstLastEvent(self::EVENT_DIVORCE, self::SORT_MAX);
177    }
178
179    /**
180     * Returns the formatted year of the first/last occuring event.
181     *
182     * @param string $type      The fact to query
183     * @param string $operation The sorting operation
184     *
185     * @return string
186     */
187    private function getFirstLastEventYear(string $type, string $operation): string
188    {
189        $row = $this->eventQuery($type, $operation);
190
191        if (!$row) {
192            return '';
193        }
194
195        if ($row->year < 0) {
196            $row->year = abs($row->year) . ' B.C.';
197        }
198
199        return (new Date($row->type . ' ' . $row->year))
200            ->display();
201    }
202
203    /**
204     * @inheritDoc
205     */
206    public function firstBirthYear(): string
207    {
208        return $this->getFirstLastEventYear(self::EVENT_BIRTH, self::SORT_MIN);
209    }
210
211    /**
212     * @inheritDoc
213     */
214    public function lastBirthYear(): string
215    {
216        return $this->getFirstLastEventYear(self::EVENT_BIRTH, self::SORT_MAX);
217    }
218
219    /**
220     * @inheritDoc
221     */
222    public function firstDeathYear(): string
223    {
224        return $this->getFirstLastEventYear(self::EVENT_DEATH, self::SORT_MIN);
225    }
226
227    /**
228     * @inheritDoc
229     */
230    public function lastDeathYear(): string
231    {
232        return $this->getFirstLastEventYear(self::EVENT_DEATH, self::SORT_MAX);
233    }
234
235    /**
236     * @inheritDoc
237     */
238    public function firstMarriageYear(): string
239    {
240        return $this->getFirstLastEventYear(self::EVENT_MARRIAGE, self::SORT_MIN);
241    }
242
243    /**
244     * @inheritDoc
245     */
246    public function lastMarriageYear(): string
247    {
248        return $this->getFirstLastEventYear(self::EVENT_MARRIAGE, self::SORT_MAX);
249    }
250
251    /**
252     * @inheritDoc
253     */
254    public function firstDivorceYear(): string
255    {
256        return $this->getFirstLastEventYear(self::EVENT_DIVORCE, self::SORT_MIN);
257    }
258
259    /**
260     * @inheritDoc
261     */
262    public function lastDivorceYear(): string
263    {
264        return $this->getFirstLastEventYear(self::EVENT_DIVORCE, self::SORT_MAX);
265    }
266
267    /**
268     * Returns the formatted name of the first/last occuring event.
269     *
270     * @param string $type      The fact to query
271     * @param string $operation The sorting operation
272     *
273     * @return string
274     */
275    private function getFirstLastEventName(string $type, string $operation): string
276    {
277        $row = $this->eventQuery($type, $operation);
278
279        if ($row) {
280            $record = GedcomRecord::getInstance($row->id, $this->tree);
281
282            if ($record) {
283                return '<a href="' . e($record->url()) . '">' . $record->fullName() . '</a>';
284            }
285        }
286
287        return '';
288    }
289
290    /**
291     * @inheritDoc
292     */
293    public function firstBirthName(): string
294    {
295        return $this->getFirstLastEventName(self::EVENT_BIRTH, self::SORT_MIN);
296    }
297
298    /**
299     * @inheritDoc
300     */
301    public function lastBirthName(): string
302    {
303        return $this->getFirstLastEventName(self::EVENT_BIRTH, self::SORT_MAX);
304    }
305
306    /**
307     * @inheritDoc
308     */
309    public function firstDeathName(): string
310    {
311        return $this->getFirstLastEventName(self::EVENT_DEATH, self::SORT_MIN);
312    }
313
314    /**
315     * @inheritDoc
316     */
317    public function lastDeathName(): string
318    {
319        return $this->getFirstLastEventName(self::EVENT_DEATH, self::SORT_MAX);
320    }
321
322    /**
323     * @inheritDoc
324     */
325    public function firstMarriageName(): string
326    {
327        return $this->getFirstLastEventName(self::EVENT_MARRIAGE, self::SORT_MIN);
328    }
329
330    /**
331     * @inheritDoc
332     */
333    public function lastMarriageName(): string
334    {
335        return $this->getFirstLastEventName(self::EVENT_MARRIAGE, self::SORT_MAX);
336    }
337
338    /**
339     * @inheritDoc
340     */
341    public function firstDivorceName(): string
342    {
343        return $this->getFirstLastEventName(self::EVENT_DIVORCE, self::SORT_MIN);
344    }
345
346    /**
347     * @inheritDoc
348     */
349    public function lastDivorceName(): string
350    {
351        return $this->getFirstLastEventName(self::EVENT_DIVORCE, self::SORT_MAX);
352    }
353
354    /**
355     * Returns the formatted place of the first/last occuring event.
356     *
357     * @param string $type      The fact to query
358     * @param string $operation The sorting operation
359     *
360     * @return string
361     */
362    private function getFirstLastEventPlace(string $type, string $operation): string
363    {
364        $row = $this->eventQuery($type, $operation);
365
366        if ($row) {
367            $record = GedcomRecord::getInstance($row->id, $this->tree);
368            $fact   = null;
369
370            if ($record) {
371                $fact = $record->facts([$row->fact])->first();
372            }
373
374            if ($fact instanceof Fact) {
375                return FunctionsPrint::formatFactPlace($fact, true, true, true);
376            }
377        }
378
379        return I18N::translate('This information is private and cannot be shown.');
380    }
381
382    /**
383     * @inheritDoc
384     */
385    public function firstBirthPlace(): string
386    {
387        return $this->getFirstLastEventPlace(self::EVENT_BIRTH, self::SORT_MIN);
388    }
389
390    /**
391     * @inheritDoc
392     */
393    public function lastBirthPlace(): string
394    {
395        return $this->getFirstLastEventPlace(self::EVENT_BIRTH, self::SORT_MAX);
396    }
397
398    /**
399     * @inheritDoc
400     */
401    public function firstDeathPlace(): string
402    {
403        return $this->getFirstLastEventPlace(self::EVENT_DEATH, self::SORT_MIN);
404    }
405
406    /**
407     * @inheritDoc
408     */
409    public function lastDeathPlace(): string
410    {
411        return $this->getFirstLastEventPlace(self::EVENT_DEATH, self::SORT_MAX);
412    }
413
414    /**
415     * @inheritDoc
416     */
417    public function firstMarriagePlace(): string
418    {
419        return $this->getFirstLastEventPlace(self::EVENT_MARRIAGE, self::SORT_MIN);
420    }
421
422    /**
423     * @inheritDoc
424     */
425    public function lastMarriagePlace(): string
426    {
427        return $this->getFirstLastEventPlace(self::EVENT_MARRIAGE, self::SORT_MAX);
428    }
429
430    /**
431     * @inheritDoc
432     */
433    public function firstDivorcePlace(): string
434    {
435        return $this->getFirstLastEventPlace(self::EVENT_DIVORCE, self::SORT_MIN);
436    }
437
438    /**
439     * @inheritDoc
440     */
441    public function lastDivorcePlace(): string
442    {
443        return $this->getFirstLastEventPlace(self::EVENT_DIVORCE, self::SORT_MAX);
444    }
445}
446