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