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