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