338 lines
10 KiB
PHP
338 lines
10 KiB
PHP
<?php
|
||
|
||
use App\Models\GeneralSetting;
|
||
use App\Models\ParentCategory;
|
||
use App\Models\Category;
|
||
use App\Models\Slide;
|
||
use App\Models\Post;
|
||
use Carbon\Carbon;
|
||
use Illuminate\Support\Str;
|
||
use App\Models\SiteSocialLink;
|
||
|
||
|
||
/**
|
||
* Einstellungen
|
||
* Lädt die allgemeinen Website-Einstellungen aus der Datenbank.
|
||
*
|
||
|
||
* Hinweis:
|
||
* - Die Funktion gibt aktuell nur dann etwas zurück, wenn Settings existieren.
|
||
* Andernfalls endet sie ohne Return (entspricht in PHP: null).
|
||
*
|
||
* @example
|
||
* $title = settings()?->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.='
|
||
<li class="nav-item dropdown">
|
||
<a class="nav-link" href="#!" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||
'.$item->name.' <i class="ti-angle-down ml-1"></i>
|
||
</a>
|
||
<div class="dropdown-menu">
|
||
';
|
||
|
||
foreach($item->children as $category) {
|
||
if($category->posts->count() > 0) {
|
||
$navigations_html.='<a class="dropdown-item" href="'.route('category_posts', $category->slug).'">'.$category->name.'</a>';
|
||
}
|
||
}
|
||
|
||
$navigations_html.='
|
||
</div>
|
||
</li>
|
||
';
|
||
}
|
||
}
|
||
|
||
if(count($categories) > 0) {
|
||
foreach($categories as $item) {
|
||
$navigations_html.='
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="'.route('category_posts', $item->slug).'">'.$item->name.'</a>
|
||
</li>
|
||
';
|
||
}
|
||
}
|
||
|
||
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();
|
||
}
|
||
}
|
||
?>
|