Files
Fitnessblog/app/Helpers/common.php
2026-01-07 15:46:00 +01:00

338 lines
10 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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();
}
}
?>