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