Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
28.57% covered (danger)
28.57%
12 / 42
25.00% covered (danger)
25.00%
2 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
PlanFeatureRepository
28.57% covered (danger)
28.57%
12 / 42
25.00% covered (danger)
25.00%
2 / 8
55.10
0.00% covered (danger)
0.00%
0 / 1
 getByPlanId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getByPlanIdWithFeatures
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 findByPlanAndFeature
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 assignFeature
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 syncFeatures
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 removeFeature
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 removeAllFeaturesFromPlan
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFeatureValue
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace App\Http\Repositories;
4
5use App\Http\Models\Feature;
6use App\Http\Models\PlanFeature;
7use App\Http\Repositories\interfaces\IPlanFeatureRepository;
8use Illuminate\Support\Collection;
9
10/**
11 * Repository for plan feature (pivot) data access operations.
12 *
13 * This repository handles all database queries related to plan-feature
14 * relationships, keeping data access logic separate from business logic.
15 */
16class PlanFeatureRepository implements IPlanFeatureRepository
17{
18    /**
19     * Get all plan features for a specific plan.
20     *
21     * @param  string  $planId  The plan ID
22     * @return Collection<int, PlanFeature> Collection of plan features
23     */
24    public function getByPlanId(string $planId): Collection
25    {
26        return PlanFeature::where('plan_id', $planId)->get();
27    }
28
29    /**
30     * Get all plan features for a specific plan with features loaded.
31     *
32     * @param  string  $planId  The plan ID
33     * @return Collection<int, PlanFeature> Collection of plan features with feature relation
34     */
35    public function getByPlanIdWithFeatures(string $planId): Collection
36    {
37        return PlanFeature::where('plan_id', $planId)
38            ->with('feature')
39            ->get();
40    }
41
42    /**
43     * Find a specific plan feature by plan and feature ID.
44     *
45     * @param  string  $planId  The plan ID
46     * @param  string  $featureId  The feature ID
47     * @return PlanFeature|null The plan feature or null if not found
48     */
49    public function findByPlanAndFeature(string $planId, string $featureId): ?PlanFeature
50    {
51        return PlanFeature::where('plan_id', $planId)
52            ->where('feature_id', $featureId)
53            ->first();
54    }
55
56    /**
57     * Assign a feature to a plan with a specific value.
58     *
59     * @param  string  $planId  The plan ID
60     * @param  string  $featureId  The feature ID
61     * @param  mixed  $value  The value for this feature on this plan
62     * @param  bool  $isEnabled  Whether the feature is enabled (default: true)
63     * @return PlanFeature The created or updated plan feature
64     */
65    public function assignFeature(string $planId, string $featureId, mixed $value, bool $isEnabled = true): PlanFeature
66    {
67        return PlanFeature::updateOrCreate(
68            [
69                'plan_id' => $planId,
70                'feature_id' => $featureId,
71            ],
72            [
73                'value' => $value,
74                'is_enabled' => $isEnabled,
75            ]
76        );
77    }
78
79    /**
80     * Sync features for a plan (replaces all existing feature assignments).
81     *
82     * @param  string  $planId  The plan ID
83     * @param  array<array{feature_id: string, value: mixed, is_enabled?: bool}>  $features  Array of feature assignments
84     * @return Collection<int, PlanFeature> Collection of synced plan features
85     */
86    public function syncFeatures(string $planId, array $features): Collection
87    {
88        // Remove all existing features for this plan
89        $this->removeAllFeaturesFromPlan($planId);
90
91        // Create new feature assignments
92        $planFeatures = collect();
93
94        foreach ($features as $featureData) {
95            $planFeature = PlanFeature::create([
96                'plan_id' => $planId,
97                'feature_id' => $featureData['feature_id'],
98                'value' => $featureData['value'],
99                'is_enabled' => $featureData['is_enabled'] ?? true,
100            ]);
101
102            $planFeatures->push($planFeature);
103        }
104
105        return $planFeatures;
106    }
107
108    /**
109     * Remove a feature from a plan.
110     *
111     * @param  string  $planId  The plan ID
112     * @param  string  $featureId  The feature ID
113     * @return bool True if removed successfully
114     */
115    public function removeFeature(string $planId, string $featureId): bool
116    {
117        return PlanFeature::where('plan_id', $planId)
118            ->where('feature_id', $featureId)
119            ->delete() > 0;
120    }
121
122    /**
123     * Remove all features from a plan.
124     *
125     * @param  string  $planId  The plan ID
126     * @return int Number of features removed
127     */
128    public function removeAllFeaturesFromPlan(string $planId): int
129    {
130        return PlanFeature::where('plan_id', $planId)->delete();
131    }
132
133    /**
134     * Get the value for a specific feature on a plan.
135     *
136     * @param  string  $planId  The plan ID
137     * @param  string  $featureKey  The feature key
138     * @return mixed The feature value or null if not found
139     */
140    public function getFeatureValue(string $planId, string $featureKey): mixed
141    {
142        // First find the feature by key
143        $feature = Feature::where('key', $featureKey)->first();
144
145        if (! $feature) {
146            return null;
147        }
148
149        // Then find the plan feature assignment
150        $planFeature = PlanFeature::where('plan_id', $planId)
151            ->where('feature_id', $feature->_id)
152            ->where('is_enabled', true)
153            ->first();
154
155        if (! $planFeature) {
156            return null;
157        }
158
159        // Return the custom value if set, otherwise the feature default
160        return $planFeature->value ?? $feature->default_value;
161    }
162}