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) 112 && ($type !== self::MEDIA_TYPE_UNKNOWN) 113 && !in_array($type, self::MEDIA_TYPES, true) 114 ) { 115 return 0; 116 } 117 118 $query = DB::table('media') 119 ->where('m_file', '=', $this->tree->id()); 120 121 if ($type !== self::MEDIA_TYPE_ALL) { 122 if ($type === self::MEDIA_TYPE_UNKNOWN) { 123 // There has to be a better way then this :( 124 foreach (self::MEDIA_TYPES as $t) { 125 // Use function to add brackets 126 $query->where(static function (Builder $query) use ($t): void { 127 $query->where('m_gedcom', 'not like', '%3 TYPE ' . $t . '%') 128 ->where('m_gedcom', 'not like', '%1 _TYPE ' . $t . '%'); 129 }); 130 } 131 } else { 132 // Use function to add brackets 133 $query->where(static function (Builder $query) use ($type): void { 134 $query->where('m_gedcom', 'like', '%3 TYPE ' . $type . '%') 135 ->orWhere('m_gedcom', 'like', '%1 _TYPE ' . $type . '%'); 136 }); 137 } 138 } 139 140 return $query->count(); 141 } 142 143 /** 144 * @inheritDoc 145 */ 146 public function totalMedia(): string 147 { 148 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_ALL)); 149 } 150 151 /** 152 * @inheritDoc 153 */ 154 public function totalMediaAudio(): string 155 { 156 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_AUDIO)); 157 } 158 159 /** 160 * @inheritDoc 161 */ 162 public function totalMediaBook(): string 163 { 164 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_BOOK)); 165 } 166 167 /** 168 * @inheritDoc 169 */ 170 public function totalMediaCard(): string 171 { 172 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_CARD)); 173 } 174 175 /** 176 * @inheritDoc 177 */ 178 public function totalMediaCertificate(): string 179 { 180 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_CERTIFICATE)); 181 } 182 183 /** 184 * @inheritDoc 185 */ 186 public function totalMediaCoatOfArms(): string 187 { 188 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_COAT)); 189 } 190 191 /** 192 * @inheritDoc 193 */ 194 public function totalMediaDocument(): string 195 { 196 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_DOCUMENT)); 197 } 198 199 /** 200 * @inheritDoc 201 */ 202 public function totalMediaElectronic(): string 203 { 204 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_ELECTRONIC)); 205 } 206 207 /** 208 * @inheritDoc 209 */ 210 public function totalMediaFiche(): string 211 { 212 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_FICHE)); 213 } 214 215 /** 216 * @inheritDoc 217 */ 218 public function totalMediaFilm(): string 219 { 220 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_FILM)); 221 } 222 223 /** 224 * @inheritDoc 225 */ 226 public function totalMediaMagazine(): string 227 { 228 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_MAGAZINE)); 229 } 230 231 /** 232 * @inheritDoc 233 */ 234 public function totalMediaManuscript(): string 235 { 236 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_MANUSCRIPT)); 237 } 238 239 /** 240 * @inheritDoc 241 */ 242 public function totalMediaMap(): string 243 { 244 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_MAP)); 245 } 246 247 /** 248 * @inheritDoc 249 */ 250 public function totalMediaNewspaper(): string 251 { 252 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_NEWSPAPER)); 253 } 254 255 /** 256 * @inheritDoc 257 */ 258 public function totalMediaPainting(): string 259 { 260 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_PAINTING)); 261 } 262 263 /** 264 * @inheritDoc 265 */ 266 public function totalMediaPhoto(): string 267 { 268 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_PHOTO)); 269 } 270 271 /** 272 * @inheritDoc 273 */ 274 public function totalMediaTombstone(): string 275 { 276 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_TOMBSTONE)); 277 } 278 279 /** 280 * @inheritDoc 281 */ 282 public function totalMediaVideo(): string 283 { 284 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_VIDEO)); 285 } 286 287 /** 288 * @inheritDoc 289 */ 290 public function totalMediaOther(): string 291 { 292 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_OTHER)); 293 } 294 295 /** 296 * @inheritDoc 297 */ 298 public function totalMediaUnknown(): string 299 { 300 return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_UNKNOWN)); 301 } 302 303 /** 304 * Returns a sorted list of media types and their total counts. 305 * 306 * @param int $tot The total number of media files 307 * 308 * @return array 309 */ 310 private function getSortedMediaTypeList(int $tot): array 311 { 312 $media = []; 313 $c = 0; 314 $max = 0; 315 316 foreach (self::MEDIA_TYPES as $type) { 317 $count = $this->totalMediaTypeQuery($type); 318 319 if ($count > 0) { 320 $media[$type] = $count; 321 322 if ($count > $max) { 323 $max = $count; 324 } 325 326 $c += $count; 327 } 328 } 329 330 $count = $this->totalMediaTypeQuery(self::MEDIA_TYPE_UNKNOWN); 331 if ($count > 0) { 332 $media[self::MEDIA_TYPE_UNKNOWN] = $tot - $c; 333 if ($tot - $c > $max) { 334 $max = $count; 335 } 336 } 337 338 if (count($media) > 10 && ($max / $tot) > 0.6) { 339 arsort($media); 340 $media = array_slice($media, 0, 10); 341 $c = $tot; 342 343 foreach ($media as $cm) { 344 $c -= $cm; 345 } 346 347 if (isset($media[self::MEDIA_TYPE_OTHER])) { 348 $media[self::MEDIA_TYPE_OTHER] += $c; 349 } else { 350 $media[self::MEDIA_TYPE_OTHER] = $c; 351 } 352 } 353 354 asort($media); 355 356 return $media; 357 } 358 359 /** 360 * @inheritDoc 361 */ 362 public function chartMedia(string $color_from = null, string $color_to = null): string 363 { 364 $tot = $this->totalMediaTypeQuery(self::MEDIA_TYPE_ALL); 365 $media = $this->getSortedMediaTypeList($tot); 366 367 return (new ChartMedia()) 368 ->chartMedia($media, $color_from, $color_to); 369 } 370} 371