xref: /webtrees/app/Statistics/Repository/MediaRepository.php (revision 1a218474113038005e50986fff24ebcbd58554ff)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2019 webtrees development team
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17declare(strict_types=1);
18
19namespace Fisharebest\Webtrees\Statistics\Repository;
20
21use Fisharebest\Webtrees\I18N;
22use Fisharebest\Webtrees\Statistics\Google\ChartMedia;
23use Fisharebest\Webtrees\Statistics\Repository\Interfaces\MediaRepositoryInterface;
24use Fisharebest\Webtrees\Tree;
25use Illuminate\Database\Capsule\Manager as DB;
26use Illuminate\Database\Query\Builder;
27
28use function array_slice;
29use function count;
30use function in_array;
31
32/**
33 * A repository providing methods for media type related statistics.
34 */
35class MediaRepository implements MediaRepositoryInterface
36{
37    /**
38     * @var Tree
39     */
40    private $tree;
41
42    /**
43     * Available media types.
44     */
45    private const MEDIA_TYPE_ALL         = 'all';
46    private const MEDIA_TYPE_AUDIO       = 'audio';
47    private const MEDIA_TYPE_BOOK        = 'book';
48    private const MEDIA_TYPE_CARD        = 'card';
49    private const MEDIA_TYPE_CERTIFICATE = 'certificate';
50    private const MEDIA_TYPE_COAT        = 'coat';
51    private const MEDIA_TYPE_DOCUMENT    = 'document';
52    private const MEDIA_TYPE_ELECTRONIC  = 'electronic';
53    private const MEDIA_TYPE_FICHE       = 'fiche';
54    private const MEDIA_TYPE_FILM        = 'film';
55    private const MEDIA_TYPE_MAGAZINE    = 'magazine';
56    private const MEDIA_TYPE_MANUSCRIPT  = 'manuscript';
57    private const MEDIA_TYPE_MAP         = 'map';
58    private const MEDIA_TYPE_NEWSPAPER   = 'newspaper';
59    private const MEDIA_TYPE_PAINTING    = 'painting';
60    private const MEDIA_TYPE_PHOTO       = 'photo';
61    private const MEDIA_TYPE_TOMBSTONE   = 'tombstone';
62    private const MEDIA_TYPE_VIDEO       = 'video';
63    private const MEDIA_TYPE_OTHER       = 'other';
64    private const MEDIA_TYPE_UNKNOWN     = 'unknown';
65
66    /**
67     * List of GEDCOM media types.
68     *
69     * @var string[]
70     */
71    private const MEDIA_TYPES = [
72        self::MEDIA_TYPE_AUDIO,
73        self::MEDIA_TYPE_BOOK,
74        self::MEDIA_TYPE_CARD,
75        self::MEDIA_TYPE_CERTIFICATE,
76        self::MEDIA_TYPE_COAT,
77        self::MEDIA_TYPE_DOCUMENT,
78        self::MEDIA_TYPE_ELECTRONIC,
79        self::MEDIA_TYPE_FICHE,
80        self::MEDIA_TYPE_FILM,
81        self::MEDIA_TYPE_MAGAZINE,
82        self::MEDIA_TYPE_MANUSCRIPT,
83        self::MEDIA_TYPE_MAP,
84        self::MEDIA_TYPE_NEWSPAPER,
85        self::MEDIA_TYPE_PAINTING,
86        self::MEDIA_TYPE_PHOTO,
87        self::MEDIA_TYPE_TOMBSTONE,
88        self::MEDIA_TYPE_VIDEO,
89        self::MEDIA_TYPE_OTHER,
90    ];
91
92    /**
93     * Constructor.
94     *
95     * @param Tree $tree
96     */
97    public function __construct(Tree $tree)
98    {
99        $this->tree = $tree;
100    }
101
102    /**
103     * Returns the number of media records of the given type.
104     *
105     * @param string $type The media type to query
106     *
107     * @return int
108     */
109    private function totalMediaTypeQuery(string $type): int
110    {
111        if ($type !== self::MEDIA_TYPE_ALL && $type !== self::MEDIA_TYPE_UNKNOWN && !in_array($type, self::MEDIA_TYPES, true)) {
112            return 0;
113        }
114
115        $query = DB::table('media')
116            ->where('m_file', '=', $this->tree->id());
117
118        if ($type !== self::MEDIA_TYPE_ALL) {
119            if ($type === self::MEDIA_TYPE_UNKNOWN) {
120                // There has to be a better way then this :(
121                foreach (self::MEDIA_TYPES as $t) {
122                    // Use function to add brackets
123                    $query->where(static function (Builder $query) use ($t): void {
124                        $query->where('m_gedcom', 'not like', '%3 TYPE ' . $t . '%')
125                            ->where('m_gedcom', 'not like', '%1 _TYPE ' . $t . '%');
126                    });
127                }
128            } else {
129                // Use function to add brackets
130                $query->where(static function (Builder $query) use ($type): void {
131                    $query->where('m_gedcom', 'like', '%3 TYPE ' . $type . '%')
132                        ->orWhere('m_gedcom', 'like', '%1 _TYPE ' . $type . '%');
133                });
134            }
135        }
136
137        return $query->count();
138    }
139
140    /**
141     * @inheritDoc
142     */
143    public function totalMedia(): string
144    {
145        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_ALL));
146    }
147
148    /**
149     * @inheritDoc
150     */
151    public function totalMediaAudio(): string
152    {
153        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_AUDIO));
154    }
155
156    /**
157     * @inheritDoc
158     */
159    public function totalMediaBook(): string
160    {
161        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_BOOK));
162    }
163
164    /**
165     * @inheritDoc
166     */
167    public function totalMediaCard(): string
168    {
169        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_CARD));
170    }
171
172    /**
173     * @inheritDoc
174     */
175    public function totalMediaCertificate(): string
176    {
177        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_CERTIFICATE));
178    }
179
180    /**
181     * @inheritDoc
182     */
183    public function totalMediaCoatOfArms(): string
184    {
185        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_COAT));
186    }
187
188    /**
189     * @inheritDoc
190     */
191    public function totalMediaDocument(): string
192    {
193        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_DOCUMENT));
194    }
195
196    /**
197     * @inheritDoc
198     */
199    public function totalMediaElectronic(): string
200    {
201        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_ELECTRONIC));
202    }
203
204    /**
205     * @inheritDoc
206     */
207    public function totalMediaFiche(): string
208    {
209        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_FICHE));
210    }
211
212    /**
213     * @inheritDoc
214     */
215    public function totalMediaFilm(): string
216    {
217        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_FILM));
218    }
219
220    /**
221     * @inheritDoc
222     */
223    public function totalMediaMagazine(): string
224    {
225        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_MAGAZINE));
226    }
227
228    /**
229     * @inheritDoc
230     */
231    public function totalMediaManuscript(): string
232    {
233        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_MANUSCRIPT));
234    }
235
236    /**
237     * @inheritDoc
238     */
239    public function totalMediaMap(): string
240    {
241        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_MAP));
242    }
243
244    /**
245     * @inheritDoc
246     */
247    public function totalMediaNewspaper(): string
248    {
249        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_NEWSPAPER));
250    }
251
252    /**
253     * @inheritDoc
254     */
255    public function totalMediaPainting(): string
256    {
257        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_PAINTING));
258    }
259
260    /**
261     * @inheritDoc
262     */
263    public function totalMediaPhoto(): string
264    {
265        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_PHOTO));
266    }
267
268    /**
269     * @inheritDoc
270     */
271    public function totalMediaTombstone(): string
272    {
273        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_TOMBSTONE));
274    }
275
276    /**
277     * @inheritDoc
278     */
279    public function totalMediaVideo(): string
280    {
281        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_VIDEO));
282    }
283
284    /**
285     * @inheritDoc
286     */
287    public function totalMediaOther(): string
288    {
289        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_OTHER));
290    }
291
292    /**
293     * @inheritDoc
294     */
295    public function totalMediaUnknown(): string
296    {
297        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_UNKNOWN));
298    }
299
300    /**
301     * Returns a sorted list of media types and their total counts.
302     *
303     * @param int $tot The total number of media files
304     *
305     * @return array
306     */
307    private function getSortedMediaTypeList(int $tot): array
308    {
309        $media = [];
310        $c     = 0;
311        $max   = 0;
312
313        foreach (self::MEDIA_TYPES as $type) {
314            $count = $this->totalMediaTypeQuery($type);
315
316            if ($count > 0) {
317                $media[$type] = $count;
318
319                if ($count > $max) {
320                    $max = $count;
321                }
322
323                $c += $count;
324            }
325        }
326
327        $count = $this->totalMediaTypeQuery(self::MEDIA_TYPE_UNKNOWN);
328        if ($count > 0) {
329            $media[self::MEDIA_TYPE_UNKNOWN] = $tot - $c;
330            if ($tot - $c > $max) {
331                $max = $count;
332            }
333        }
334
335        if (count($media) > 10 && ($max / $tot) > 0.6) {
336            arsort($media);
337            $media = array_slice($media, 0, 10);
338            $c     = $tot;
339
340            foreach ($media as $cm) {
341                $c -= $cm;
342            }
343
344            if (isset($media[self::MEDIA_TYPE_OTHER])) {
345                $media[self::MEDIA_TYPE_OTHER] += $c;
346            } else {
347                $media[self::MEDIA_TYPE_OTHER] = $c;
348            }
349        }
350
351        asort($media);
352
353        return $media;
354    }
355
356    /**
357     * @inheritDoc
358     */
359    public function chartMedia(string $color_from = null, string $color_to = null): string
360    {
361        $tot   = $this->totalMediaTypeQuery(self::MEDIA_TYPE_ALL);
362        $media = $this->getSortedMediaTypeList($tot);
363
364        return (new ChartMedia())
365            ->chartMedia($media, $color_from, $color_to);
366    }
367}
368