From bb0f8a6322442f5c0708c3571f33a2240aff115e Mon Sep 17 00:00:00 2001 From: Jan Wennrich <42703348+JanWennrich@users.noreply.github.com> Date: Sat, 19 Oct 2024 23:54:31 +0200 Subject: [PATCH] feat: split downloading data and rendering page into separate scripts Related: #2 --- Makefile | 9 ++- assets/boardgames-owned.serialized.txt | 1 + assets/boardgames-played.serialized.txt | 1 + composer.json | 5 +- composer.lock | 91 ++++++++++++++++++++++++- container-config.php | 21 ++++++ download-bgg-data.php | 23 +++++++ generate-html.php | 57 ++++++---------- src/BoardgameCollection.php | 19 ++++++ src/HtmlGenerator.php | 14 ++-- src/OwnedBoardgamesLoader.php | 7 +- src/OwnedBoardgamesLoaderInterface.php | 5 +- src/PlayCollection.php | 19 ++++++ src/PlayedBoardgamesLoader.php | 7 +- src/PlayedBoardgamesLoaderInterface.php | 5 +- 15 files changed, 219 insertions(+), 65 deletions(-) create mode 100644 assets/boardgames-owned.serialized.txt create mode 100644 assets/boardgames-played.serialized.txt create mode 100644 container-config.php create mode 100644 download-bgg-data.php create mode 100644 src/BoardgameCollection.php create mode 100644 src/PlayCollection.php diff --git a/Makefile b/Makefile index f847277..887dd65 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,14 @@ build-page: + $(MAKE) clean + $(MAKE) download-bgg-data + $(MAKE) generate-html + +download-bgg-data: + php download-bgg-data.php +generate-html: $(MAKE) clean mkdir build - php generate-html.php > build/index.html + php generate-html.php cp -r assets/* build/ clean: rm -rf build \ No newline at end of file diff --git a/assets/boardgames-owned.serialized.txt b/assets/boardgames-owned.serialized.txt new file mode 100644 index 0000000..761ef45 --- /dev/null +++ b/assets/boardgames-owned.serialized.txt @@ -0,0 +1 @@ +O:42:"JanWennrich\BoardGames\BoardgameCollection":44:{i:0;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:34:"Adventure Games: Expedition Azcana";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/1eAiUKQz8FzuqokQEKNn8Q__thumb/img/xE9uQkxgc_Rj59BtbP8TLzlS0RQ=/fit-in/200x150/filters:strip_icc()/pic6881571.jpg";}i:1;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:30:"Adventure Games: Im Nebelreich";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/hokAc4M0sSShWHlWRU3wNQ__thumb/img/fBoTEFy9dnrIJ_ry57Iezenpnxg=/fit-in/200x150/filters:strip_icc()/pic6482624.jpg";}i:2;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:32:"Adventure Games: Monochrome Inc.";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/43pBOAx1RiVj4CB9twXErw__thumb/img/0FemvM2YohBC0RZGQfc7s5c2Of4=/fit-in/200x150/filters:strip_icc()/pic4597112.jpg";}i:3;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:28:"Adventure Games: The Dungeon";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/YdQ2gMORorqOgEecLAm-cA__thumb/img/wmgERsyZjtOyBnOAFaC0ttZwV0Y=/fit-in/200x150/filters:strip_icc()/pic7056314.jpg";}i:4;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:36:"Adventure Games: The Gloom City File";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/voSgOcpYelkNfpmeQWgOAA__thumb/img/D_RMRpF340Etb0KMgfkM4wmPRE4=/fit-in/200x150/filters:strip_icc()/pic6630213.jpg";}i:5;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:40:"Adventure Games: The Grand Hotel Abaddon";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/dKRPsLgTrkFVAwUqU4BBqQ__thumb/img/u2VDEylxdcE3AJlrVL3iWzYatFQ=/fit-in/200x150/filters:strip_icc()/pic6210950.jpg";}i:6;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:36:"Adventure Games: The Volcanic Island";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/8fchTYyEfkzZ9ZWj8xsVPQ__thumb/img/3JkrMAzE8RNk0hkXdKS6tfwNVqc=/fit-in/200x150/filters:strip_icc()/pic5145286.jpg";}i:7;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:13:"Black Stories";s:12:"thumbnailUrl";s:140:"https://cf.geekdo-images.com/NPnm8Pz2-uvMsbeptOX0fw__thumb/img/DusuyQ7kHboKxDKt70GOvbsEck8=/fit-in/200x150/filters:strip_icc()/pic208686.jpg";}i:8;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:28:"Black Stories: Movie Edition";s:12:"thumbnailUrl";s:140:"https://cf.geekdo-images.com/zE5k42_3u83WePAvuvB2ZA__thumb/img/3byVSPNU9LaEzqh8ObS1InweAfA=/fit-in/200x150/filters:strip_icc()/pic628264.jpg";}i:9;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:23:"Cartaventura: Hollywood";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/DpgHUfAV1ccLT8hl-6v63Q__thumb/img/owUNNZwgCV9oA0ZPEH2EDkrsFLk=/fit-in/200x150/filters:strip_icc()/pic7042265.jpg";}i:10;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:21:"Cartaventura: Vinland";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/oF6c53g6uloTuY8l2o19Fg__thumb/img/lkksBKC3VcYmd2f4HjaH3JkzAjE=/fit-in/200x150/filters:strip_icc()/pic6630228.jpg";}i:11;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:13:"Cartographers";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/GifbnAmsA4lfEcDkeaC9VA__thumb/img/TTxZzwbna07hMcPQ0xaFtT10egE=/fit-in/200x150/filters:strip_icc()/pic4397932.png";}i:12;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:8:"Cascadia";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/MjeJZfulbsM1DSV3DrGJYA__thumb/img/tVSFjSxYEcw7sKj3unIIQV8kxoc=/fit-in/200x150/filters:strip_icc()/pic5100691.jpg";}i:13;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:5:"CATAN";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/W3Bsga_uLP9kO91gZ7H8yw__thumb/img/8a9HeqFydO7Uun_le9bXWPnidcA=/fit-in/200x150/filters:strip_icc()/pic2419375.jpg";}i:14;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:16:"CATAN: Seafarers";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/yO6X6iObEb7PtECKCagMKg__thumb/img/sADKPjQ9M3aZgwONMdYvPgmme6c=/fit-in/200x150/filters:strip_icc()/pic2420293.jpg";}i:15;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:7:"Cracked";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/Id0oqQ8eTK4ilrNB51UN-A__thumb/img/-fnR2W99CmeSK1mshgiKLRVx-CY=/fit-in/200x150/filters:strip_icc()/pic4953267.jpg";}i:16;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:11:"Do De Li Do";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/KKL-2QVDa4wa7he4iR28iw__thumb/img/RDmrT9YBcQwViIfnWFy8CR3xs-0=/fit-in/200x150/filters:strip_icc()/pic3240504.jpg";}i:17;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:25:"Dominion (Second Edition)";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/4dmyKfqQm_uxA1-chh4JeQ__thumb/img/Td5CxjO0ZTIbwHNhk2ETEy1qYyg=/fit-in/200x150/filters:strip_icc()/pic3187001.jpg";}i:18;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:18:"Dominion: Nocturne";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/V7tSsQGq_ADFByx5dvTb7w__thumb/img/x_S9T5ktKnDfH4sJG7FUlkz3o-M=/fit-in/200x150/filters:strip_icc()/pic3677538.jpg";}i:19;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:12:"Encyclopedia";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/sL-5ZjHTK3EUJDiv0tlilg__thumb/img/fVuKUiBB4TdNM7_q8KGFasCIuDc=/fit-in/200x150/filters:strip_icc()/pic6515523.png";}i:20;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:46:"Exit: The Game – The Professor's Last Riddle";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/0EcFnlbxRBajJkTqTdYhrg__thumb/img/oig4cEskzOBMNo_AQz5Rhb2q758=/fit-in/200x150/filters:strip_icc()/pic7233514.jpg";}i:21;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:17:"Exploding Kittens";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/N8bL53-pRU7zaXDTrEaYrw__thumb/img/3tH4pIc1Udzkd0tXc6MgVQ59BC0=/fit-in/200x150/filters:strip_icc()/pic2691976.png";}i:22;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:14:"Fantasy Realms";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/A7IOpPt-lHrMYsbRmxXWdQ__thumb/img/XoD3fXgApfY7-dfYyFeE6FVkbfY=/fit-in/200x150/filters:strip_icc()/pic6177962.jpg";}i:23;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:10:"Final Girl";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/TUtzY-F7gKTIKm9y8e1AQw__thumb/img/CVgcWusAt0oVteoy0XYmsFmhfQY=/fit-in/200x150/filters:strip_icc()/pic6520382.jpg";}i:24;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:35:"Final Girl: Carnage at the Carnival";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/iEGnWjR-7V4dcnN-c6-GJQ__thumb/img/x9kQ9OcO0dC_jL5dHofu0Ywe3-w=/fit-in/200x150/filters:strip_icc()/pic7486293.jpg";}i:25;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:11:"Halli Galli";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/gm-LiZvjkluOeKqPvuUjDg__thumb/img/eTHCJERXOmqmzJP-b0QQCChDhn8=/fit-in/200x150/filters:strip_icc()/pic8223481.jpg";}i:26;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:12:"ISS Vanguard";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/1ySeZNVkVwTvqykMGtHFFQ__thumb/img/pXp7JQo64YalithQyDM7tufyasA=/fit-in/200x150/filters:strip_icc()/pic7245199.jpg";}i:27;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:12:"Jungle Speed";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/pb553nZgH3akaF24MzT7nA__thumb/img/Clb-hjbKmSJcl6U6UJMqAz0Kxy0=/fit-in/200x150/filters:strip_icc()/pic4659199.jpg";}i:28;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:7:"Kayanak";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/WM7QkJWaWue1BZDUOX4LQQ__thumb/img/EnUyl_vpGRYu9rysrKDWM_tDqgc=/fit-in/200x150/filters:strip_icc()/pic1793211.jpg";}i:29;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:16:"Legends of Andor";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/IO8_BYKeofnWy8DIjo2RRg__thumb/img/pMbV-KGyO3D9U6hnBzNRfylZvto=/fit-in/200x150/filters:strip_icc()/pic2606106.jpg";}i:30;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:21:"The Lord of the Rings";s:12:"thumbnailUrl";s:140:"https://cf.geekdo-images.com/oWttjfs235Zh7jLGWO7_Hg__thumb/img/D8T2cKo8fiipSbw4vNNP9hNbZ0g=/fit-in/200x150/filters:strip_icc()/pic479124.jpg";}i:31;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:7:"Malefiz";s:12:"thumbnailUrl";s:140:"https://cf.geekdo-images.com/K7Y8l0NtpKVxiO8c1h3KSw__thumb/img/bWQOrZtgu4t5xx4uFb82Uc0f_oU=/fit-in/200x150/filters:strip_icc()/pic276070.jpg";}i:32;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:24:"Monopoly: Sankt Augustin";s:12:"thumbnailUrl";s:0:"";}i:33;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:12:"My First UNO";s:12:"thumbnailUrl";s:139:"https://cf.geekdo-images.com/J3A5pcnWEDIE-4L4VlC15Q__thumb/img/ZxUfE9sxI1Ptree81n6ccUff28Y=/fit-in/200x150/filters:strip_icc()/pic39522.jpg";}i:34;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:14:"Mysterium Park";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/U2CYPYgIEC1cLF45Rh0UGw__thumb/img/bTRB0-Qnb-tvrYxN3vJJZRBEZ1s=/fit-in/200x150/filters:strip_icc()/pic5581038.jpg";}i:35;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:8:"Phase 10";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/jvHGs95ShI8VEhDBdxKs6w__thumb/img/mjMr9BgjDUiz1ITatBLPOMe-QOw=/fit-in/200x150/filters:strip_icc()/pic7621070.png";}i:36;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:14:"Rolling Realms";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/5TZ5YfDo_r9vNNvWTl_pzw__thumb/img/oKM1pAH4fCa7LCNluCqr9qNOq4M=/fit-in/200x150/filters:strip_icc()/pic6313647.jpg";}i:37;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:8:"Spot it!";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/AKdJV9CBlZBxH87EAfZ85A__thumb/img/MMvB9deDa-BgXcwyWIUsKleqbbU=/fit-in/200x150/filters:strip_icc()/pic7807049.jpg";}i:38;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:18:"Team Work Original";s:12:"thumbnailUrl";s:140:"https://cf.geekdo-images.com/LU0hePAznzHv41UlYLTykA__thumb/img/fFxE5whFKOfuVk-t9RSdmkWvpQg=/fit-in/200x150/filters:strip_icc()/pic916260.jpg";}i:39;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:16:"Trails of Tucana";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/y8ifesXfHobUtIgnSuC0DA__thumb/img/y5rGQUFTGETcIg9HSfuGnHkD234=/fit-in/200x150/filters:strip_icc()/pic4860701.jpg";}i:40;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:6:"Unfair";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/RQvdby3WmdROu05YFuxnqQ__thumb/img/RRVq0hk9d5X9QbshiIBeWXCdmSA=/fit-in/200x150/filters:strip_icc()/pic4717882.jpg";}i:41;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:33:"The Werewolves of Miller's Hollow";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/rZ0tOafAt1oue_7NKqELsg__thumb/img/jqycRUhCBZjq1E87oQVIFb1_j0A=/fit-in/200x150/filters:strip_icc()/pic7203269.jpg";}i:42;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:8:"Wingspan";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/yLZJCVLlIx4c7eJEWUNJ7w__thumb/img/VNToqgS2-pOGU6MuvIkMPKn_y-s=/fit-in/200x150/filters:strip_icc()/pic4458123.jpg";}i:43;O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:28:"Wingspan: European Expansion";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/7izK8WM_bgtvBzxQvLGz-A__thumb/img/Q76pV0p00R8qkcS6o-nczJD7DCk=/fit-in/200x150/filters:strip_icc()/pic4982682.jpg";}} \ No newline at end of file diff --git a/assets/boardgames-played.serialized.txt b/assets/boardgames-played.serialized.txt new file mode 100644 index 0000000..fb7c3bd --- /dev/null +++ b/assets/boardgames-played.serialized.txt @@ -0,0 +1 @@ +O:37:"JanWennrich\BoardGames\PlayCollection":71:{i:0;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:15:"Eldritch Horror";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/oxdsZKIX5_3ovDo38EIaeg__thumb/img/I1oCwOcGxAOVx2VE2LcXuKWoLvY=/fit-in/200x150/filters:strip_icc()/pic1872452.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-10-18 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:1;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:35:"Final Girl: Carnage at the Carnival";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/iEGnWjR-7V4dcnN-c6-GJQ__thumb/img/x9kQ9OcO0dC_jL5dHofu0Ywe3-w=/fit-in/200x150/filters:strip_icc()/pic7486293.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-10-17 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:2;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:23:"Cartaventura: Hollywood";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/DpgHUfAV1ccLT8hl-6v63Q__thumb/img/owUNNZwgCV9oA0ZPEH2EDkrsFLk=/fit-in/200x150/filters:strip_icc()/pic7042265.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-10-12 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:3;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:14:"Mysterium Park";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/U2CYPYgIEC1cLF45Rh0UGw__thumb/img/bTRB0-Qnb-tvrYxN3vJJZRBEZ1s=/fit-in/200x150/filters:strip_icc()/pic5581038.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-10-12 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:4;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:23:"Cartaventura: Hollywood";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/DpgHUfAV1ccLT8hl-6v63Q__thumb/img/owUNNZwgCV9oA0ZPEH2EDkrsFLk=/fit-in/200x150/filters:strip_icc()/pic7042265.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-10-11 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:5;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:19:"Cartaventura: Lhasa";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/j_CBzavdQQu7RnKaIddABA__thumb/img/oa4B_yI4gfjUYOosUSAdRRY4Euw=/fit-in/200x150/filters:strip_icc()/pic6635984.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-10-08 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:6;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:22:"MicroMacro: Crime City";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/1M-1d6fr3U9t670MCVOTeg__thumb/img/aR9LNVhRr_hnIIoYwxFUTb8rPMk=/fit-in/200x150/filters:strip_icc()/pic5896816.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-10-08 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:7;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:14:"Mysterium Park";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/U2CYPYgIEC1cLF45Rh0UGw__thumb/img/bTRB0-Qnb-tvrYxN3vJJZRBEZ1s=/fit-in/200x150/filters:strip_icc()/pic5581038.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-10-08 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:8;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:28:"Gloomhaven: Jaws of the Lion";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/_HhIdavYW-hid20Iq3hhmg__thumb/img/OMkN_E5eyWrkID_cHCnQEbIixGM=/fit-in/200x150/filters:strip_icc()/pic5055631.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-10-06 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:9;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:12:"ISS Vanguard";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/1ySeZNVkVwTvqykMGtHFFQ__thumb/img/pXp7JQo64YalithQyDM7tufyasA=/fit-in/200x150/filters:strip_icc()/pic7245199.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-10-06 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:10;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:20:"Dorfromantik: Sakura";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/fznRvIx6a-rU56Ly0RqpTA__thumb/img/VkEezaIwaCX_6MZj2vcrcSb9nQk=/fit-in/200x150/filters:strip_icc()/pic8305438.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-10-05 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:11;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:21:"Escape Room: The Game";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/1CIfreuv-gqcaUndxUAExw__thumb/img/ve4j9SWc6dtPQhF-fRcRjPmqUxc=/fit-in/200x150/filters:strip_icc()/pic3117400.png";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-10-05 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:12;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:8:"Voidfall";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/hItZjdDTNuaCZ7fEztwcUQ__thumb/img/Ov6eWR87PZ2O1XYQtXMO6KvfzOg=/fit-in/200x150/filters:strip_icc()/pic6153324.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-09-21 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:13;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:31:"T.I.M.E Stories: Estrella Drive";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/HhQ8ajIs_Jiqyl4DpGEQjQ__thumb/img/MdEu4vcibTlNMZV1nsj-RSPzZvs=/fit-in/200x150/filters:strip_icc()/pic4601037.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-09-08 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:14;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:28:"Gloomhaven: Jaws of the Lion";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/_HhIdavYW-hid20Iq3hhmg__thumb/img/OMkN_E5eyWrkID_cHCnQEbIixGM=/fit-in/200x150/filters:strip_icc()/pic5055631.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-08-25 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:15;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:33:"Twilight Imperium: Fourth Edition";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/_Ppn5lssO5OaildSE-FgFA__thumb/img/lfEukJE0JsoZZObaF9K9YnFp62E=/fit-in/200x150/filters:strip_icc()/pic3727516.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-07-28 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:16;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:19:"Cartaventura: Lhasa";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/j_CBzavdQQu7RnKaIddABA__thumb/img/oa4B_yI4gfjUYOosUSAdRRY4Euw=/fit-in/200x150/filters:strip_icc()/pic6635984.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-04-30 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:17;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:33:"Twilight Imperium: Fourth Edition";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/_Ppn5lssO5OaildSE-FgFA__thumb/img/lfEukJE0JsoZZObaF9K9YnFp62E=/fit-in/200x150/filters:strip_icc()/pic3727516.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-04-28 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:18;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:6:"Unfair";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/RQvdby3WmdROu05YFuxnqQ__thumb/img/RRVq0hk9d5X9QbshiIBeWXCdmSA=/fit-in/200x150/filters:strip_icc()/pic4717882.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-04-13 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:19;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:31:"T.I.M.E Stories: Estrella Drive";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/HhQ8ajIs_Jiqyl4DpGEQjQ__thumb/img/MdEu4vcibTlNMZV1nsj-RSPzZvs=/fit-in/200x150/filters:strip_icc()/pic4601037.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-03-30 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:20;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:15:"T.I.M.E Stories";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/XmwCsjQ3C1Ul53ZN2o8Q-Q__thumb/img/tTU1YVtab-cN5gCdagINowvVA2g=/fit-in/200x150/filters:strip_icc()/pic2617634.png";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-03-29 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:21;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:10:"Zooloretto";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/WiO4FLeGBs-_88UQdpHrcA__thumb/img/Vb6YHpZ06cB_iYWigESl3ZCyNeY=/fit-in/200x150/filters:strip_icc()/pic7758113.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-03-19 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:22;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:26:"Codenames: Deep Undercover";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/Hnxruo8BBx73kd4TCZQdYg__thumb/img/kKE4VDsVRyDbQFxNLNzIIl-L85w=/fit-in/200x150/filters:strip_icc()/pic4390414.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-03-16 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:23;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:22:"Risk (Revised Edition)";s:12:"thumbnailUrl";s:140:"https://cf.geekdo-images.com/DiCHx8FTBAz1kMbaSMH5ew__thumb/img/Fp8SKaKa4jb417OF6lVW__-Lj54=/fit-in/200x150/filters:strip_icc()/pic347837.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-03-16 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:24;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:21:"Cartaventura: Vinland";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/oF6c53g6uloTuY8l2o19Fg__thumb/img/lkksBKC3VcYmd2f4HjaH3JkzAjE=/fit-in/200x150/filters:strip_icc()/pic6630228.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-03-10 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:25;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:8:"Cascadia";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/MjeJZfulbsM1DSV3DrGJYA__thumb/img/tVSFjSxYEcw7sKj3unIIQV8kxoc=/fit-in/200x150/filters:strip_icc()/pic5100691.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-03-10 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:26;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:22:"Abandon All Artichokes";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/qwFz_zhMjIxRjgAIx__8WQ__thumb/img/PPRNQrHRGq48IhY6PlOXg95aYUw=/fit-in/200x150/filters:strip_icc()/pic5335069.png";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-03-01 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:27;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:21:"Cartaventura: Vinland";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/oF6c53g6uloTuY8l2o19Fg__thumb/img/lkksBKC3VcYmd2f4HjaH3JkzAjE=/fit-in/200x150/filters:strip_icc()/pic6630228.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-03-01 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:28;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:21:"The Lord of the Rings";s:12:"thumbnailUrl";s:140:"https://cf.geekdo-images.com/oWttjfs235Zh7jLGWO7_Hg__thumb/img/D8T2cKo8fiipSbw4vNNP9hNbZ0g=/fit-in/200x150/filters:strip_icc()/pic479124.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-03-01 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:29;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:12:"No Prob-Lama";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/p22Itsw3YRDyZFwMpCOUuA__thumb/img/AMjL0XjLxMmLe5YLDmsNjdFaC6g=/fit-in/200x150/filters:strip_icc()/pic4682324.png";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-03-01 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:30;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:15:"T.I.M.E Stories";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/XmwCsjQ3C1Ul53ZN2o8Q-Q__thumb/img/tTU1YVtab-cN5gCdagINowvVA2g=/fit-in/200x150/filters:strip_icc()/pic2617634.png";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-02-19 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:31;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:33:"Twilight Imperium: Fourth Edition";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/_Ppn5lssO5OaildSE-FgFA__thumb/img/lfEukJE0JsoZZObaF9K9YnFp62E=/fit-in/200x150/filters:strip_icc()/pic3727516.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-01-20 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:32;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:45:"Exit: The Game – The Hunt Through Amsterdam";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/bLdXaCeY85qaVPAFoRYFIg__thumb/img/dW2M7QE_dL0jbyw8JkHIU9nL248=/fit-in/200x150/filters:strip_icc()/pic7962706.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-01-06 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:33;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:8:"Wingspan";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/yLZJCVLlIx4c7eJEWUNJ7w__thumb/img/VNToqgS2-pOGU6MuvIkMPKn_y-s=/fit-in/200x150/filters:strip_icc()/pic4458123.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2024-01-06 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:34;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:34:"Adventure Games: Expedition Azcana";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/1eAiUKQz8FzuqokQEKNn8Q__thumb/img/xE9uQkxgc_Rj59BtbP8TLzlS0RQ=/fit-in/200x150/filters:strip_icc()/pic6881571.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:35;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:30:"Adventure Games: Im Nebelreich";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/hokAc4M0sSShWHlWRU3wNQ__thumb/img/fBoTEFy9dnrIJ_ry57Iezenpnxg=/fit-in/200x150/filters:strip_icc()/pic6482624.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:36;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:32:"Adventure Games: Monochrome Inc.";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/43pBOAx1RiVj4CB9twXErw__thumb/img/0FemvM2YohBC0RZGQfc7s5c2Of4=/fit-in/200x150/filters:strip_icc()/pic4597112.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:37;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:28:"Adventure Games: The Dungeon";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/YdQ2gMORorqOgEecLAm-cA__thumb/img/wmgERsyZjtOyBnOAFaC0ttZwV0Y=/fit-in/200x150/filters:strip_icc()/pic7056314.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:38;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:36:"Adventure Games: The Gloom City File";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/voSgOcpYelkNfpmeQWgOAA__thumb/img/D_RMRpF340Etb0KMgfkM4wmPRE4=/fit-in/200x150/filters:strip_icc()/pic6630213.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:39;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:40:"Adventure Games: The Grand Hotel Abaddon";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/dKRPsLgTrkFVAwUqU4BBqQ__thumb/img/u2VDEylxdcE3AJlrVL3iWzYatFQ=/fit-in/200x150/filters:strip_icc()/pic6210950.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:40;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:36:"Adventure Games: The Volcanic Island";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/8fchTYyEfkzZ9ZWj8xsVPQ__thumb/img/3JkrMAzE8RNk0hkXdKS6tfwNVqc=/fit-in/200x150/filters:strip_icc()/pic5145286.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:41;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:13:"Black Stories";s:12:"thumbnailUrl";s:140:"https://cf.geekdo-images.com/NPnm8Pz2-uvMsbeptOX0fw__thumb/img/DusuyQ7kHboKxDKt70GOvbsEck8=/fit-in/200x150/filters:strip_icc()/pic208686.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:42;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:21:"Cartaventura: Vinland";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/oF6c53g6uloTuY8l2o19Fg__thumb/img/lkksBKC3VcYmd2f4HjaH3JkzAjE=/fit-in/200x150/filters:strip_icc()/pic6630228.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:43;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:13:"Cartographers";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/GifbnAmsA4lfEcDkeaC9VA__thumb/img/TTxZzwbna07hMcPQ0xaFtT10egE=/fit-in/200x150/filters:strip_icc()/pic4397932.png";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:44;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:8:"Cascadia";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/MjeJZfulbsM1DSV3DrGJYA__thumb/img/tVSFjSxYEcw7sKj3unIIQV8kxoc=/fit-in/200x150/filters:strip_icc()/pic5100691.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:45;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:7:"Cracked";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/Id0oqQ8eTK4ilrNB51UN-A__thumb/img/-fnR2W99CmeSK1mshgiKLRVx-CY=/fit-in/200x150/filters:strip_icc()/pic4953267.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:46;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:25:"Dominion (Second Edition)";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/4dmyKfqQm_uxA1-chh4JeQ__thumb/img/Td5CxjO0ZTIbwHNhk2ETEy1qYyg=/fit-in/200x150/filters:strip_icc()/pic3187001.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:47;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:18:"Dominion: Nocturne";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/V7tSsQGq_ADFByx5dvTb7w__thumb/img/x_S9T5ktKnDfH4sJG7FUlkz3o-M=/fit-in/200x150/filters:strip_icc()/pic3677538.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:48;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:49:"Exit: The Game – Dead Man on the Orient Express";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/atCG4ujYJ-L5LKID3ouz8g__thumb/img/m1IWCmDXxZ-jXpvPGvM6mU_HySQ=/fit-in/200x150/filters:strip_icc()/pic5320930.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:49;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:38:"Exit: The Game – The Abandoned Cabin";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/r7cZVZMpP28w3-JQ-EYY7g__thumb/img/_XH-DqIVWOmaNX9P3CxNt9OpOQ4=/fit-in/200x150/filters:strip_icc()/pic3332426.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:50;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:45:"Exit: The Game – The Cemetery of the Knight";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/qTyIMMQ9N-EC0cCaPv9NDQ__thumb/img/RRVAHj0A8D0XziXenyEGPxHfwcU=/fit-in/200x150/filters:strip_icc()/pic5536690.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:51;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:39:"Exit: The Game – The Forbidden Castle";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/9rVXzuVqnFkF9WQbk8Y1qg__thumb/img/Z6EauUoGg3nN0Rcl5UoBsiEufbg=/fit-in/200x150/filters:strip_icc()/pic3663362.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:52;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:45:"Exit: The Game – The Haunted Roller Coaster";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/LvqhO8Rwm-DcUoVWksOuEQ__thumb/img/t2AFoi54g-BC4zQPvznmKBogCdo=/fit-in/200x150/filters:strip_icc()/pic4597147.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:53;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:37:"Exit: The Game – The Pharaoh's Tomb";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/fGO2BYF3whgNaSBZMVSO9g__thumb/img/UTICjkO5c4q01KusZw3NePiFvUg=/fit-in/200x150/filters:strip_icc()/pic3332423.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:54;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:36:"Exit: The Game – The Polar Station";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/CrqSZVCqD9PJDhGOPBVyHQ__thumb/img/Yb2NHVS4yOWjuHLDJi2CDXU9gR4=/fit-in/200x150/filters:strip_icc()/pic3663361.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:55;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:33:"Exit: The Game – The Secret Lab";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/3c1Wy_dXjVZ8Fpytyf9UvQ__thumb/img/nsHTKDzkuv_eo-nkzGMA1ZBcsPk=/fit-in/200x150/filters:strip_icc()/pic3332425.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:56;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:36:"Exit: The Game – The Stormy Flight";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/7kTyrflx3HjvkIHzkTCSkA__thumb/img/Ouy7YOo9Tu9UvDlXvDYH6gYz744=/fit-in/200x150/filters:strip_icc()/pic5300129.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:57;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:38:"Exit: The Game – The Sunken Treasure";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/Qy3-Sz1IZB5v4n3uAZ29TA__thumb/img/9-fbF7biR5vs6tfCk5MR0PlOIHw=/fit-in/200x150/filters:strip_icc()/pic3958802.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:58;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:43:"Exit: The Game – Theft on the Mississippi";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/lAtgcsdOBVgepR_9smwWmw__thumb/img/MAtZmX3WMvuetx-VHaM48cf2Le4=/fit-in/200x150/filters:strip_icc()/pic6210951.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:59;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:51:"Exit: The Game + Puzzle – The Deserted Lighthouse";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/N4TEqRBnrlSFqBzaESWnfA__thumb/img/ctGU_TGPsn9WwITVGSu1tzfB6mg=/fit-in/200x150/filters:strip_icc()/pic5988904.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:60;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:45:"Exit: The Game + Puzzle – The Sacred Temple";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/2G_lBcFfgiEo8ubbtE7J5g__thumb/img/6aBOGO9qxWWOI1kEMka-oZlhx7o=/fit-in/200x150/filters:strip_icc()/pic5988905.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:61;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:17:"Exploding Kittens";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/N8bL53-pRU7zaXDTrEaYrw__thumb/img/3tH4pIc1Udzkd0tXc6MgVQ59BC0=/fit-in/200x150/filters:strip_icc()/pic2691976.png";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:62;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:11:"Halli Galli";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/gm-LiZvjkluOeKqPvuUjDg__thumb/img/eTHCJERXOmqmzJP-b0QQCChDhn8=/fit-in/200x150/filters:strip_icc()/pic8223481.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:63;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:7:"Kayanak";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/WM7QkJWaWue1BZDUOX4LQQ__thumb/img/EnUyl_vpGRYu9rysrKDWM_tDqgc=/fit-in/200x150/filters:strip_icc()/pic1793211.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:64;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:7:"Malefiz";s:12:"thumbnailUrl";s:140:"https://cf.geekdo-images.com/K7Y8l0NtpKVxiO8c1h3KSw__thumb/img/bWQOrZtgu4t5xx4uFb82Uc0f_oU=/fit-in/200x150/filters:strip_icc()/pic276070.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:65;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:8:"Phase 10";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/jvHGs95ShI8VEhDBdxKs6w__thumb/img/mjMr9BgjDUiz1ITatBLPOMe-QOw=/fit-in/200x150/filters:strip_icc()/pic7621070.png";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:66;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:8:"Spot it!";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/AKdJV9CBlZBxH87EAfZ85A__thumb/img/MMvB9deDa-BgXcwyWIUsKleqbbU=/fit-in/200x150/filters:strip_icc()/pic7807049.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:67;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:16:"Trails of Tucana";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/y8ifesXfHobUtIgnSuC0DA__thumb/img/y5rGQUFTGETcIg9HSfuGnHkD234=/fit-in/200x150/filters:strip_icc()/pic4860701.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:68;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:33:"The Werewolves of Miller's Hollow";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/rZ0tOafAt1oue_7NKqELsg__thumb/img/jqycRUhCBZjq1E87oQVIFb1_j0A=/fit-in/200x150/filters:strip_icc()/pic7203269.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-31 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:69;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:11:"Do De Li Do";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/KKL-2QVDa4wa7he4iR28iw__thumb/img/RDmrT9YBcQwViIfnWFy8CR3xs-0=/fit-in/200x150/filters:strip_icc()/pic3240504.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-26 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}i:70;O:27:"JanWennrich\BoardGames\Play":2:{s:9:"boardgame";O:32:"JanWennrich\BoardGames\Boardgame":2:{s:5:"title";s:64:"Exit: The Game – Advent Calendar: The Hunt for the Golden Book";s:12:"thumbnailUrl";s:141:"https://cf.geekdo-images.com/nNVkEVfayrcrfOzFs_14Bw__thumb/img/IWoGQDOs60xPPLUZ2qAqR61wEck=/fit-in/200x150/filters:strip_icc()/pic6632318.jpg";}s:12:"playDateTime";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-12-24 00:00:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}}} \ No newline at end of file diff --git a/composer.json b/composer.json index 87ce38c..e4fe1cc 100644 --- a/composer.json +++ b/composer.json @@ -4,9 +4,10 @@ "type": "project", "require": { "php": "^8.2", + "nataniel/bggxmlapi2": "^1.1.5", "php-di/php-di": "^7.0.7", - "twig/twig": "^3.14.0", - "nataniel/bggxmlapi2": "^1.1.5" + "ramsey/collection": "^2.0", + "twig/twig": "^3.14.0" }, "require-dev": { "captainhook/captainhook": "^5.23.5", diff --git a/composer.lock b/composer.lock index c3090d2..82dc233 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a93a1d1201a5948d6171124f0246abc1", + "content-hash": "365d28f82798ca4e06634c027ca64413", "packages": [ { "name": "laravel/serializable-closure", @@ -293,6 +293,95 @@ }, "time": "2021-11-05T16:47:00+00:00" }, + { + "name": "ramsey/collection", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.28.3", + "fakerphp/faker": "^1.21", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^1.0", + "mockery/mockery": "^1.5", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcsstandards/phpcsutils": "^1.0.0-rc1", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2022-12-31T21:50:55+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v3.5.0", diff --git a/container-config.php b/container-config.php new file mode 100644 index 0000000..2f47a03 --- /dev/null +++ b/container-config.php @@ -0,0 +1,21 @@ + \DI\env('BGG_USERNAME'), + 'serialization.path.boardgames.played' => 'assets/boardgames-played.serialized.txt', + 'serialization.path.boardgames.owned' => 'assets/boardgames-owned.serialized.txt', + Environment::class => \DI\factory(function () { + return new Environment(new FilesystemLoader(__DIR__ . '/templates')); + }), + PlayedBoardgamesLoaderInterface::class => \DI\get(PlayedBoardgamesLoader::class), + OwnedBoardgamesLoaderInterface::class => \DI\get(OwnedBoardgamesLoader::class), +]; diff --git a/download-bgg-data.php b/download-bgg-data.php new file mode 100644 index 0000000..e081624 --- /dev/null +++ b/download-bgg-data.php @@ -0,0 +1,23 @@ +get('bgg.username'); + +$playedBoardgames = $diContainer->get(PlayedBoardgamesLoader::class)->getForUser($bggUsername); +$playedBoardgamesSerialized = serialize($playedBoardgames); +$playedBoardgamesSerializationPath = $diContainer->get('serialization.path.boardgames.played'); +file_put_contents($playedBoardgamesSerializationPath, $playedBoardgamesSerialized); + +$ownedBoardgames = $diContainer->get(OwnedBoardgamesLoader::class)->getForUser($bggUsername); +$ownedBoardgamesSerialized = serialize($ownedBoardgames); +$ownedBoardgamesSerializationPath = $diContainer->get('serialization.path.boardgames.owned'); +file_put_contents($ownedBoardgamesSerializationPath, $ownedBoardgamesSerialized); diff --git a/generate-html.php b/generate-html.php index 48f9e8d..a01c031 100644 --- a/generate-html.php +++ b/generate-html.php @@ -1,40 +1,23 @@ \DI\env('BGG_USERNAME'), - Environment::class => \DI\factory(function () { - return new Environment(new FilesystemLoader(__DIR__ . '/templates')); - }), - PlayedBoardgamesLoaderInterface::class => \DI\get(PlayedBoardgamesLoader::class), - OwnedBoardgamesLoaderInterface::class => \DI\get(OwnedBoardgamesLoader::class), -]); - -if (getenv('BGG_API') === 'mocked') { - $diContainer->set( - PlayedBoardgamesLoaderInterface::class, - \DI\get(PlayedBoardgamesLoaderStub::class), - ); - - $diContainer->set( - OwnedBoardgamesLoaderInterface::class, - \DI\get(OwnedBoardgamesLoaderStub::class), - ); -} - -$bggUsername = $diContainer->get('bgg.username'); - -$html = $diContainer->get(HtmlGenerator::class)->generateHtml($bggUsername); - -echo $html; + +require_once __DIR__ . '/vendor/autoload.php'; + +$containerConfig = require_once __DIR__ . "/container-config.php"; + +$diContainer = new \DI\Container($containerConfig); + +$boardgamesOwnerSerializedPath = $diContainer->get('serialization.path.boardgames.owned'); +$boardgamesOwnedSerialized = file_get_contents($boardgamesOwnerSerializedPath); +$boardgamesOwned = unserialize($boardgamesOwnedSerialized ?: throw new Exception("Could not read owned boardgames from '$boardgamesOwnerSerializedPath'")); + +$boardgamesPlayedSerializedPath = $diContainer->get('serialization.path.boardgames.played'); +$boardgamesPlayedSerialized = file_get_contents($boardgamesPlayedSerializedPath); +$boardgamesPlayed = unserialize($boardgamesPlayedSerialized ?: throw new Exception("Could not read played boardgames from '$boardgamesPlayedSerializedPath'")); + +$htmlGenerator = $diContainer->get(HtmlGenerator::class); +$html = $htmlGenerator->generateHtml($boardgamesOwned, $boardgamesPlayed); + +file_put_contents(__DIR__ . '/build/index.html', $html); diff --git a/src/BoardgameCollection.php b/src/BoardgameCollection.php new file mode 100644 index 0000000..69be1e5 --- /dev/null +++ b/src/BoardgameCollection.php @@ -0,0 +1,19 @@ + + */ +class BoardgameCollection extends AbstractCollection +{ + /** + * @return class-string + */ + public function getType(): string + { + return Boardgame::class; + } +} diff --git a/src/HtmlGenerator.php b/src/HtmlGenerator.php index 40106ad..1704255 100644 --- a/src/HtmlGenerator.php +++ b/src/HtmlGenerator.php @@ -7,27 +7,21 @@ final readonly class HtmlGenerator { public function __construct( - private Environment $twig, - private PlayedBoardgamesLoaderInterface $plays, - private OwnedBoardgamesLoaderInterface $ownedBoardgamesLoader + private Environment $twig ) { } - public function generateHtml(string $bggUsername): string + public function generateHtml(BoardgameCollection $boardgamesOwned, PlayCollection $boardgamesPlayed): string { - $ownedBoardgames = $this->ownedBoardgamesLoader->getForUser($bggUsername); - $ownedBoardgamesGroupedByFirstLetter = []; - foreach ($ownedBoardgames as $ownedBoardgame) { + foreach ($boardgamesOwned as $ownedBoardgame) { $ownedBoardgamesGroupedByFirstLetter[mb_strtolower($ownedBoardgame->title[0])][] = $ownedBoardgame; } - $plays = $this->plays->getForUser($bggUsername); - $playsGroupedByDate = []; - foreach ($plays as $play) { + foreach ($boardgamesPlayed as $play) { $playsGroupedByDate[$play->playDateTime->format('d.m.y')][] = $play->boardgame; } diff --git a/src/OwnedBoardgamesLoader.php b/src/OwnedBoardgamesLoader.php index ec74334..6c8d231 100644 --- a/src/OwnedBoardgamesLoader.php +++ b/src/OwnedBoardgamesLoader.php @@ -13,22 +13,23 @@ public function __construct( } /** - * @return Boardgame[] * @throws \Nataniel\BoardGameGeek\Exception */ - public function getForUser(string $bggUsername): array + public function getForUser(string $bggUsername): BoardgameCollection { $ownedBoardgames = $this->bggApiClient->getCollection([ 'username' => $bggUsername, 'own' => 1, ]); - return array_map( + $ownedBoardgames = array_map( fn(CollectionItem $collectionItem) => new Boardgame( $collectionItem->getName(), $collectionItem->getThumbnail(), ), $ownedBoardgames, ); + + return new BoardgameCollection($ownedBoardgames); } } diff --git a/src/OwnedBoardgamesLoaderInterface.php b/src/OwnedBoardgamesLoaderInterface.php index aeb03ec..8da8d15 100644 --- a/src/OwnedBoardgamesLoaderInterface.php +++ b/src/OwnedBoardgamesLoaderInterface.php @@ -4,8 +4,5 @@ interface OwnedBoardgamesLoaderInterface { - /** - * @return Boardgame[] - */ - public function getForUser(string $bggUsername): array; + public function getForUser(string $bggUsername): BoardgameCollection; } diff --git a/src/PlayCollection.php b/src/PlayCollection.php new file mode 100644 index 0000000..42bd0b5 --- /dev/null +++ b/src/PlayCollection.php @@ -0,0 +1,19 @@ + + */ +class PlayCollection extends AbstractCollection +{ + /** + * @return class-string + */ + public function getType(): string + { + return Play::class; + } +} diff --git a/src/PlayedBoardgamesLoader.php b/src/PlayedBoardgamesLoader.php index 3e67891..96a728f 100644 --- a/src/PlayedBoardgamesLoader.php +++ b/src/PlayedBoardgamesLoader.php @@ -13,10 +13,9 @@ public function __construct( } /** - * @return Play[] * @throws \Nataniel\BoardGameGeek\Exception|\DateMalformedStringException */ - public function getForUser(string $bggUsername): array + public function getForUser(string $bggUsername): PlayCollection { $bggPlays = $this->bggApiClient->getPlays(['username' => $bggUsername]); @@ -24,7 +23,7 @@ public function getForUser(string $bggUsername): array $playedGamesThumbnails = $this->thumbnailLoader->getForBggGameIds($bggPlayedGamesIds); - return array_map( + $plays = array_map( function (\Nataniel\BoardGameGeek\Play $bggPlay) use ($playedGamesThumbnails) { return new Play( new Boardgame($bggPlay->getObjectName(), $playedGamesThumbnails[$bggPlay->getObjectId()]), @@ -33,5 +32,7 @@ function (\Nataniel\BoardGameGeek\Play $bggPlay) use ($playedGamesThumbnails) { }, $bggPlays, ); + + return new PlayCollection($plays); } } diff --git a/src/PlayedBoardgamesLoaderInterface.php b/src/PlayedBoardgamesLoaderInterface.php index 7d18735..e018750 100644 --- a/src/PlayedBoardgamesLoaderInterface.php +++ b/src/PlayedBoardgamesLoaderInterface.php @@ -4,8 +4,5 @@ interface PlayedBoardgamesLoaderInterface { - /** - * @return Play[] - */ - public function getForUser(string $bggUsername): array; + public function getForUser(string $bggUsername): PlayCollection; }