Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
PlanComparisonController
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
3 / 3
4
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 comparison
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 export
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace App\Http\Controllers\v2\Admin;
4
5use App\Http\Controllers\Controller;
6use App\Http\Requests\v2\Plan\Admin\PlanComparisonExportRequest;
7use App\Http\Requests\v2\Plan\Admin\PlanComparisonRequest;
8use App\Http\Resources\v2\PlanComparisonResource;
9use App\Http\Services\CsvExportService;
10use App\Http\Services\PlanComparisonService;
11use Symfony\Component\HttpFoundation\StreamedResponse;
12
13/**
14 * Controller for plan comparison reports (admin).
15 *
16 * Only accessible by users with the VENGRESO_ADMIN role.
17 * Provides comparison matrix and CSV export for analyzing features across plans.
18 */
19class PlanComparisonController extends Controller
20{
21    public function __construct(
22        private PlanComparisonService $comparisonService,
23        private CsvExportService $csvExportService
24    ) {}
25
26    /**
27     * Get plan comparison matrix.
28     *
29     * Returns a comparison of features across specified plans, including
30     * a matrix showing feature values, differences, and common features.
31     *
32     * @param  PlanComparisonRequest  $request  Validated request with filter options
33     *
34     * @response 200 {
35     *   "result": {
36     *     "plans": [
37     *       {"id": "...", "title": "Freemium", "identifier": "freemium", "currency": "usd", "interval": "month", "unit_amount": 0}
38     *     ],
39     *     "features": [
40     *       {"key": "bold", "name": "Bold Text", "description": "Enable bold formatting", "value_type": "boolean", "category": "formatting", "display_order": 1}
41     *     ],
42     *     "comparison_matrix": {
43     *       "bold": {
44     *         "feature_key": "bold",
45     *         "feature_name": "Bold Text",
46     *         "category": "formatting",
47     *         "value_type": "boolean",
48     *         "is_common": true,
49     *         "values": {
50     *           "freemium": {"raw": true, "display": "Yes"},
51     *           "growth": {"raw": true, "display": "Yes"}
52     *         }
53     *       }
54     *     },
55     *     "differences": [
56     *       {"feature_key": "shortcuts", "feature_name": "Shortcuts Limit", "category": "limits", "values": {"freemium": 10, "growth": -1}}
57     *     ],
58     *     "common_features": ["bold", "italic"],
59     *     "hubspot_status": {
60     *       "freemium": {"has_config": true, "properties_count": 5, "name": "Freemium"}
61     *     },
62     *     "summary": {
63     *       "total_plans": 2,
64     *       "total_features": 15,
65     *       "common_features_count": 10,
66     *       "different_features_count": 5,
67     *       "common_percentage": 66.7,
68     *       "categories_covered": ["formatting", "limits"]
69     *     }
70     *   }
71     * }
72     */
73    public function comparison(PlanComparisonRequest $request): PlanComparisonResource
74    {
75        $comparison = $this->comparisonService->generateComparison(
76            planIds: $request->getPlanIds(),
77            category: $request->getCategory(),
78            activeOnly: $request->isActiveOnly(),
79            includeHubspot: $request->shouldIncludeHubspot()
80        );
81
82        return new PlanComparisonResource($comparison);
83    }
84
85    /**
86     * Export plan comparison as CSV.
87     *
88     * Returns a downloadable CSV file with the plan comparison matrix.
89     *
90     * @param  PlanComparisonExportRequest  $request  Validated request with filter options
91     *
92     * @response 200 CSV file download
93     */
94    public function export(PlanComparisonExportRequest $request): StreamedResponse
95    {
96        $csvData = $this->comparisonService->exportToCsv(
97            planIds: $request->getPlanIds(),
98            category: $request->getCategory(),
99            activeOnly: $request->isActiveOnly(),
100            includeHubspot: $request->shouldIncludeHubspot()
101        );
102
103        $filename = 'plan_comparison_'.now()->format('Y-m-d').'.csv';
104
105        $delimiter = $this->csvExportService->resolveDelimiter($request->user());
106
107        $callback = function () use ($csvData, $delimiter) {
108            $file = fopen('php://output', 'w');
109
110            // Add BOM for Excel compatibility
111            fprintf($file, chr(0xEF).chr(0xBB).chr(0xBF));
112
113            // Write headers
114            fputcsv($file, $csvData['headers'], $delimiter);
115
116            // Write data rows
117            foreach ($csvData['rows'] as $row) {
118                fputcsv($file, $row, $delimiter);
119            }
120
121            fclose($file);
122        };
123
124        return new StreamedResponse($callback, 200, [
125            'Content-Type' => 'text/csv',
126            'Content-Disposition' => 'attachment; filename="'.$filename.'"',
127        ]);
128    }
129}