Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
36.36% covered (danger)
36.36%
4 / 11
66.67% covered (warning)
66.67%
4 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Feature
36.36% covered (danger)
36.36%
4 / 11
66.67% covered (warning)
66.67%
4 / 6
15.28
0.00% covered (danger)
0.00%
0 / 1
 plans
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 planFeatures
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 scopeActive
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 scopeCategory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 scopeOrdered
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newFactory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Http\Models;
4
5use Database\Factories\Http\Models\FeatureFactory;
6use Illuminate\Database\Eloquent\Factories\HasFactory;
7use Illuminate\Database\Eloquent\Relations\BelongsToMany;
8
9/**
10 * Feature model for managing plan features.
11 *
12 * Represents a feature that can be assigned to plans with custom values.
13 * Features define capabilities, limits, and formatting options available
14 * to users based on their subscription plan.
15 *
16 * @property string $_id The feature ID
17 * @property string $key Unique identifier key (e.g., "character_limit", "bold")
18 * @property string $name Human-readable name (e.g., "Character Limit")
19 * @property string|null $description Description of the feature
20 * @property string $value_type Type of value: boolean, integer, string, array, object
21 * @property string $category Feature category: formatting, limits, storage, ai, general
22 * @property mixed $default_value Default value when not overridden by plan
23 * @property bool $is_active Whether the feature is active
24 * @property int $display_order Order for display purposes
25 * @property \Carbon\Carbon|null $created_at
26 * @property \Carbon\Carbon|null $updated_at
27 */
28class Feature extends Moloquent
29{
30    use HasFactory;
31
32    /**
33     * Value type constants.
34     */
35    public const VALUE_TYPE_BOOLEAN = 'boolean';
36
37    public const VALUE_TYPE_INTEGER = 'integer';
38
39    public const VALUE_TYPE_STRING = 'string';
40
41    public const VALUE_TYPE_ARRAY = 'array';
42
43    public const VALUE_TYPE_OBJECT = 'object';
44
45    /**
46     * Valid value types.
47     *
48     * @var array<string>
49     */
50    public const VALUE_TYPES = [
51        self::VALUE_TYPE_BOOLEAN,
52        self::VALUE_TYPE_INTEGER,
53        self::VALUE_TYPE_STRING,
54        self::VALUE_TYPE_ARRAY,
55        self::VALUE_TYPE_OBJECT,
56    ];
57
58    /**
59     * Category constants.
60     */
61    public const CATEGORY_FORMATTING = 'formatting';
62
63    public const CATEGORY_LIMITS = 'limits';
64
65    public const CATEGORY_STORAGE = 'storage';
66
67    public const CATEGORY_AI = 'ai';
68
69    public const CATEGORY_GENERAL = 'general';
70
71    /**
72     * Valid categories.
73     *
74     * @var array<string>
75     */
76    public const CATEGORIES = [
77        self::CATEGORY_FORMATTING,
78        self::CATEGORY_LIMITS,
79        self::CATEGORY_STORAGE,
80        self::CATEGORY_AI,
81        self::CATEGORY_GENERAL,
82    ];
83
84    /**
85     * The collection associated with the model.
86     *
87     * @var string
88     */
89    protected $collection = 'features';
90
91    /**
92     * The attributes that are mass assignable.
93     *
94     * @var array<string>
95     */
96    protected $fillable = [
97        'key',
98        'name',
99        'description',
100        'value_type',
101        'category',
102        'default_value',
103        'is_active',
104        'display_order',
105    ];
106
107    /**
108     * The attributes that should be cast.
109     *
110     * @var array<string, string>
111     */
112    protected $casts = [
113        'is_active' => 'boolean',
114        'display_order' => 'integer',
115    ];
116
117    /**
118     * The model's default values for attributes.
119     *
120     * @var array<string, mixed>
121     */
122    protected $attributes = [
123        'is_active' => true,
124        'display_order' => 0,
125    ];
126
127    /**
128     * Get the plans that have this feature.
129     *
130     * @return BelongsToMany<Plans>
131     */
132    public function plans(): BelongsToMany
133    {
134        return $this->belongsToMany(
135            Plans::class,
136            null,
137            'feature_ids',
138            'plan_ids'
139        );
140    }
141
142    /**
143     * Get the plan feature pivot records for this feature.
144     *
145     * @return \Illuminate\Database\Eloquent\Relations\HasMany<PlanFeature>
146     */
147    public function planFeatures(): \Illuminate\Database\Eloquent\Relations\HasMany
148    {
149        return $this->hasMany(PlanFeature::class, 'feature_id');
150    }
151
152    /**
153     * Scope a query to only include active features.
154     *
155     * @param  \Illuminate\Database\Eloquent\Builder<Feature>  $query
156     * @return \Illuminate\Database\Eloquent\Builder<Feature>
157     */
158    public function scopeActive($query)
159    {
160        return $query->where('is_active', true);
161    }
162
163    /**
164     * Scope a query to filter by category.
165     *
166     * @param  \Illuminate\Database\Eloquent\Builder<Feature>  $query
167     * @param  string  $category  The category to filter by
168     * @return \Illuminate\Database\Eloquent\Builder<Feature>
169     */
170    public function scopeCategory($query, string $category)
171    {
172        return $query->where('category', $category);
173    }
174
175    /**
176     * Scope a query to order by display order.
177     *
178     * @param  \Illuminate\Database\Eloquent\Builder<Feature>  $query
179     * @return \Illuminate\Database\Eloquent\Builder<Feature>
180     */
181    public function scopeOrdered($query)
182    {
183        return $query->orderBy('display_order');
184    }
185
186    /**
187     * Create a new factory instance for the model.
188     *
189     * @return FeatureFactory
190     */
191    protected static function newFactory()
192    {
193        return FeatureFactory::new();
194    }
195}