Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
SeoEntityTrait
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 6
462
0.00% covered (danger)
0.00%
0 / 1
 getSeoAccessibleFields
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 hasSeoContent
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 getSeoData
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 setSeoData
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 getEffectiveMetaTitle
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
42
 getEffectiveMetaDescription
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2declare(strict_types=1);
3
4namespace App\Model\Entity;
5
6/**
7 * SeoEntityTrait
8 *
9 * Provides common SEO field accessibility and helper methods for Entity classes.
10 * This trait consolidates duplicate SEO field accessibility patterns that were
11 * previously scattered across multiple entity classes.
12 */
13trait SeoEntityTrait
14{
15    /**
16     * Get the standard SEO fields accessibility array
17     *
18     * @return array<string, bool> SEO fields with accessibility set to true
19     */
20    protected function getSeoAccessibleFields(): array
21    {
22        return [
23            'meta_title' => true,
24            'meta_description' => true,
25            'meta_keywords' => true,
26            'facebook_description' => true,
27            'linkedin_description' => true,
28            'twitter_description' => true,
29            'instagram_description' => true,
30        ];
31    }
32
33    /**
34     * Check if any SEO fields have values
35     *
36     * @return bool True if at least one SEO field has a value
37     */
38    public function hasSeoContent(): bool
39    {
40        $seoFields = array_keys($this->getSeoAccessibleFields());
41
42        foreach ($seoFields as $field) {
43            if (!empty($this->{$field})) {
44                return true;
45            }
46        }
47
48        return false;
49    }
50
51    /**
52     * Get all SEO field values as an array
53     *
54     * @return array<string, string|null> Array of SEO field name => value pairs
55     */
56    public function getSeoData(): array
57    {
58        $seoFields = array_keys($this->getSeoAccessibleFields());
59        $seoData = [];
60
61        foreach ($seoFields as $field) {
62            $seoData[$field] = $this->{$field} ?? null;
63        }
64
65        return $seoData;
66    }
67
68    /**
69     * Set multiple SEO fields at once
70     *
71     * @param array<string, string> $seoData Array of SEO field name => value pairs
72     * @return $this
73     */
74    public function setSeoData(array $seoData)
75    {
76        $allowedFields = array_keys($this->getSeoAccessibleFields());
77
78        foreach ($seoData as $field => $value) {
79            if (in_array($field, $allowedFields)) {
80                $this->{$field} = $value;
81            }
82        }
83
84        return $this;
85    }
86
87    /**
88     * Get the effective meta title (falls back to title if meta_title is empty)
89     *
90     * @return string|null
91     */
92    public function getEffectiveMetaTitle(): ?string
93    {
94        if (!empty($this->meta_title)) {
95            return $this->meta_title;
96        }
97
98        // Fall back to entity title if available
99        if (property_exists($this, 'title') && !empty($this->title)) {
100            return $this->title;
101        }
102
103        // Fall back to entity name if available (for galleries)
104        if (property_exists($this, 'name') && !empty($this->name)) {
105            return $this->name;
106        }
107
108        return null;
109    }
110
111    /**
112     * Get the effective meta description (falls back to description/lede if meta_description is empty)
113     *
114     * @return string|null
115     */
116    public function getEffectiveMetaDescription(): ?string
117    {
118        if (!empty($this->meta_description)) {
119            return $this->meta_description;
120        }
121
122        // Fall back to entity description if available
123        if (property_exists($this, 'description') && !empty($this->description)) {
124            return $this->description;
125        }
126
127        // Fall back to entity lede if available (for articles)
128        if (property_exists($this, 'lede') && !empty($this->lede)) {
129            return $this->lede;
130        }
131
132        return null;
133    }
134}