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