site_title; // PHP 8+ Nullsafe Operator * $logo = settings()?->site_logo; */ if(!function_exists("settings")) { function settings() { $settings = GeneralSetting::take(1)->first(); if(!is_null($settings)) { return $settings; } } } /** * Seiten Social Link * Lädt die Social-Media-Links der Website aus der Datenbank. * * Hinweis: * - Die Funktion gibt nur dann einen Wert zurück, wenn Einträge existieren. * Andernfalls endet sie ohne expliziten Return (entspricht in PHP: null). * * @example * $facebook = site_social_links()?->facebook; * $twitter = site_social_links()?->twitter; */ if(!function_exists('site_social_links')) { function site_social_links() { $links = SiteSocialLink::take(1)->first(); if(!is_null($links)) { return $links; } } } /** * Dynamisches Navigations Menu * Generiert die HTML-Navigation basierend auf Kategorien mit Beiträgen. * * Hinweis: * - Es werden nur Kategorien berücksichtigt, die mindestens einen Beitrag haben. * - Parent-Kategorien werden als Dropdown dargestellt, sofern Kinder mit Beiträgen existieren. * - Kategorien ohne Parent werden als normale Navigationslinks ausgegeben. * * @return string HTML-Markup der Navigationsliste * * @example * {!! navigations() !!} */ if(!function_exists('navigations')) { function navigations() { $navigations_html = ''; $pcategories = ParentCategory::whereHas('children', function($q) { $q->whereHas('posts'); })->orderBy('name', 'asc')->get(); $categories = Category::whereHas('posts')->where('parent', 0)->orderBy('name', 'asc')->get(); if(count($pcategories) > 0) { foreach($pcategories as $item) { $navigations_html.=' '; } } if(count($categories) > 0) { foreach($categories as $item) { $navigations_html.=' '; } } return $navigations_html; } } /** * DATE FORMAT January 12, 2024 * Formatiert ein Datum in ein lesbares, lokalisiertes Format. * * Hinweis: * - Erwartet ein Datum im Format `Y-m-d H:i:s`. * - Die Ausgabe erfolgt im ISO-Format `LL` (z. B. „14. September 2025“). * * @param string $value Datum/Zeit im Format `Y-m-d H:i:s` * @return string Formatiertes Datum * * @example * {{ date_formatter($post->created_at) }} */ if(!function_exists('date_formatter')) { function date_formatter($value) { return Carbon::createFromFormat('Y-m-d H:i:s', $value)->isoFormat('LL'); } } /** * Kürzt Wörter * Kürzt einen Text auf eine bestimmte Anzahl von Wörtern. * * Hinweis: * - HTML-Tags werden vor der Kürzung entfernt. * - Am Ende des Textes wird optional ein Abschlusszeichen angehängt. * * @param string $value Ausgangstext * @param int $words Maximale Anzahl an Wörtern (Standard: 15) * @param string $end Angehängter Text am Ende (Standard: "...") * @return string Gekürzter Text * * @example * {{ words($post->content, 30) }} */ if(!function_exists('words')) { function words($value, $words = 15, $end = '...') { return Str::words(strip_tags($value), $words, $end); } } /** * Lesezeit * Berechnet die geschätzte Lesezeit eines Textes. * * Hinweis: * - Die Berechnung basiert auf ca. 200 Wörtern pro Minute. * - Die minimale Rückgabe beträgt immer 1 Minute. * - Mehrere Textteile können als Argumente übergeben werden. * * @param string ...$text Ein oder mehrere Textinhalte * @return int Geschätzte Lesezeit in Minuten * * @example * {{ readDuration($post->title, $post->content) }} Min. Lesezeit */ if (!function_exists('readDuration')) { function readDuration(...$text): int { // Alles zu einem String zusammenfügen $content = implode(' ', $text); // HTML-Tags entfernen $content = strip_tags($content); // Wörter zählen (für DE ausreichend gut) $totalWords = str_word_count($content); // Lesegeschwindigkeit (Wörter pro Minute) – nach Bedarf anpassen $wordsPerMinute = 220; // Minuten berechnen und immer aufrunden $minutesToRead = (int) ceil($totalWords / max(1, $wordsPerMinute)); // Mindestens 1 Minute zurückgeben return max(1, $minutesToRead); } } /** * Zeigt letzten Post an * Gibt die neuesten sichtbaren Blogbeiträge zurück. * * Hinweis: * - Es werden nur veröffentlichte Beiträge berücksichtigt (`visibility = 1`). * - Über `$skip` können Einträge übersprungen werden (z. B. für Slider oder Pagination). * * @param int $skip Anzahl der zu überspringenden Beiträge (Standard: 0) * @param int $limit Maximale Anzahl der zurückgegebenen Beiträge (Standard: 5) * @return \Illuminate\Support\Collection Sammlung von Blogbeiträgen * * @example * @foreach(latest_posts(0, 3) as $post) * {{ $post->title }} * @endforeach */ if(!function_exists('latest_posts')) { function latest_posts($skip = 0, $limit = 5) { return Post::skip($skip)->limit($limit)->where('visibility', 1)->orderBy('created_at', 'desc')->get(); } } /** * Listing Categories With Number of Posts on Sidebar * Gibt Kategorien für die Sidebar zurück, sortiert nach Beitragsanzahl. * * Hinweis: * - Es werden nur Kategorien mit mindestens einem Beitrag berücksichtigt. * - Die Sortierung erfolgt absteigend nach Anzahl der Beiträge. * * @param int $limit Maximale Anzahl der Kategorien (Standard: 8) * @return \Illuminate\Support\Collection Sammlung von Kategorien * * @example * @foreach(sidebar_categories() as $category) * {{ $category->name }} ({{ $category->posts_count }}) * @endforeach */ if(!function_exists('sidebar_categories')) { function sidebar_categories($limit = 8) { return Category::withCount('posts')->having('posts_count', '>', 0)->limit($limit)->orderBy('posts_count', 'desc')->get(); } } /** * FETCH ALL TAGS FROM THE 'POSTS' TABLE * Gibt eine eindeutige Liste aller verwendeten Tags zurück. * * Hinweis: * - Tags werden aus dem `tags`-Feld der Beiträge gelesen (kommasepariert). * - Leere Tag-Felder werden ignoriert. * - Die Rückgabe ist alphabetisch sortiert und eindeutig. * * @param int|null $limit Maximale Anzahl der Tags (optional) * @return array Liste der Tags * * @example * @foreach(getTags() as $tag) * {{ $tag }} * @endforeach */ if (!function_exists('getTags')) { function getTags($limit = null) { $tags = Post::whereNotNull('tags') ->where('tags', '!=', '') ->pluck('tags'); $uniqueTags = $tags->flatMap(function ($tagsString) { return explode(',', $tagsString); }) ->map(fn($tag) => normalizeTag($tag)) ->filter() ->unique() ->sort() ->values(); if ($limit) { $uniqueTags = $uniqueTags->take($limit); } return $uniqueTags->all(); } } function normalizeTag(string $tag): string { // urldecode: wandelt %20 und auch + -> Leerzeichen (wichtig!) $tag = urldecode($tag); // Whitespace vereinheitlichen $tag = preg_replace('/\s+/u', ' ', trim($tag)); return $tag; } /** * Listing Sidebar Latest posts * Gibt die neuesten sichtbaren Blogbeiträge für die Sidebar zurück. * * Hinweis: * - Es werden nur veröffentlichte Beiträge berücksichtigt (`visibility = 1`). * - Optional kann ein bestimmter Beitrag ausgeschlossen werden. * - Die Sortierung erfolgt nach Erstellungsdatum (neueste zuerst). * * @param int $limit Maximale Anzahl der Beiträge (Standard: 5) * @param int|null $except ID eines auszuschließenden Beitrags (optional) * @return \Illuminate\Support\Collection Sammlung von Blogbeiträgen * * @example * @foreach(sidebar_latest_posts(5, $post->id) as $item) * {{ $item->title }} * @endforeach */ if(!function_exists('sidebar_latest_posts')) { function sidebar_latest_posts($limit = 5, $except = null) { $posts = Post::limit($limit); if($except) { $posts = $posts->where('id', '!=', $except); } return $posts->where('visibility', 1)->orderBy('created_at', 'desc')->get(); } } /** * Home Slider * Gibt aktive Slider-Einträge für den Home-Slider zurück. * * Hinweis: * - Es werden nur Slides mit aktivem Status (`status = 1`) berücksichtigt. * - Die Sortierung erfolgt nach der definierten Reihenfolge (`ordering`). * * @param int $limit Maximale Anzahl der Slides (Standard: 5) * @return \Illuminate\Support\Collection Sammlung von Slides * * @example * @foreach(get_slides() as $slide) * {{ $slide->title }} * @endforeach */ if(!function_exists('get_slides')) { function get_slides($limit = 5) { return Slide::where('status', 1)->limit($limit)->orderBy('ordering', 'asc')->get(); } } ?>