Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 87
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
RoutingController
0.00% covered (danger)
0.00%
0 / 87
0.00% covered (danger)
0.00%
0 / 8
240
0.00% covered (danger)
0.00%
0 / 1
 index
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 create
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 store
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
12
 edit
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 update
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 destroy
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 toggle
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 parseJsonFilters
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Models\Feed;
6use App\Models\Route;
7use App\Models\PlatformChannel;
8use App\Services\RoutingValidationService;
9use App\Exceptions\RoutingMismatchException;
10use Illuminate\Database\Eloquent\Collection;
11use Illuminate\Http\Request;
12use Illuminate\Contracts\View\View;
13use Illuminate\Http\RedirectResponse;
14
15class RoutingController extends Controller
16{
17    public function index(): View
18    {
19        $feeds = Feed::with(['channels.platformInstance'])
20            ->where('is_active', true)
21            ->orderBy('name')
22            ->get();
23
24        $channels = PlatformChannel::with(['platformInstance', 'feeds'])
25            ->where('is_active', true)
26            ->orderBy('name')
27            ->get();
28
29        return view('pages.routing.index', compact('feeds', 'channels'));
30    }
31
32    public function create(): View
33    {
34        $feeds = Feed::where('is_active', true)
35            ->orderBy('name')
36            ->get();
37
38        $channels = PlatformChannel::with('platformInstance')
39            ->where('is_active', true)
40            ->orderBy('name')
41            ->get();
42
43        return view('pages.routing.create', compact('feeds', 'channels'));
44    }
45
46    public function store(Request $request): RedirectResponse
47    {
48        $validated = $request->validate([
49            'feed_id' => 'required|exists:feeds,id',
50            'channel_ids' => 'required|array|min:1',
51            'channel_ids.*' => 'exists:platform_channels,id',
52            'priority' => 'integer|min:0|max:100',
53            'filters' => 'nullable|string'
54        ]);
55
56        /** @var Feed $feed */
57        $feed = Feed::findOrFail($validated['feed_id']);
58        
59        /** @var Collection<int, PlatformChannel> $channels */
60        $channels = PlatformChannel::findMany($validated['channel_ids']);
61        $priority = $validated['priority'] ?? 0;
62
63        try {
64            app(RoutingValidationService::class)->validateLanguageCompatibility($feed, $channels);
65        } catch (RoutingMismatchException $e) {
66            return redirect()->back()
67                ->withInput()
68                ->withErrors(['language' => $e->getMessage()]);
69        }
70
71        $filters = $this->parseJsonFilters($validated['filters'] ?? null);
72
73        // Attach channels to feed
74        $syncData = [];
75        foreach ($validated['channel_ids'] as $channelId) {
76            $syncData[$channelId] = [
77                'is_active' => true,
78                'priority' => $priority,
79                'filters' => $filters,
80                'created_at' => now(),
81                'updated_at' => now()
82            ];
83        }
84
85        $feed->channels()->syncWithoutDetaching($syncData);
86
87        return redirect()->route('routing.index')
88            ->with('success', 'Feed routing created successfully!');
89    }
90
91    public function edit(Feed $feed, PlatformChannel $channel): View
92    {
93        $routing = $feed->channels()
94            ->wherePivot('platform_channel_id', $channel->id)
95            ->first();
96
97        if (! $routing) {
98            abort(404, 'Routing not found');
99        }
100
101        return view('pages.routing.edit', compact('feed', 'channel', 'routing'));
102    }
103
104    public function update(Request $request, Feed $feed, PlatformChannel $channel): RedirectResponse
105    {
106        $validated = $request->validate([
107            'is_active' => 'boolean',
108            'priority' => 'integer|min:0|max:100',
109            'filters' => 'nullable|string'
110        ]);
111
112        $filters = $this->parseJsonFilters($validated['filters'] ?? null);
113
114        $feed->channels()->updateExistingPivot($channel->id, [
115            'is_active' => $validated['is_active'] ?? true,
116            'priority' => $validated['priority'] ?? 0,
117            'filters' => $filters,
118            'updated_at' => now()
119        ]);
120
121        return redirect()->route('routing.index')
122            ->with('success', 'Routing updated successfully!');
123    }
124
125    public function destroy(Feed $feed, PlatformChannel $channel): RedirectResponse
126    {
127        $feed->channels()->detach($channel->id);
128
129        return redirect()->route('routing.index')
130            ->with('success', 'Routing deleted successfully!');
131    }
132
133    public function toggle(Request $request, Feed $feed, PlatformChannel $channel): RedirectResponse
134    {
135        $routing = Route::where('feed_id', $feed->id)
136            ->where('platform_channel_id', $channel->id)
137            ->first();
138
139        if (! $routing) {
140            abort(404, 'Routing not found');
141        }
142
143        $newStatus = ! $routing->is_active;
144
145        $feed->channels()->updateExistingPivot($channel->id, [
146            'is_active' => $newStatus,
147            'updated_at' => now()
148        ]);
149
150        $status = $newStatus ? 'activated' : 'deactivated';
151
152        return redirect()->route('routing.index')
153            ->with('success', "Routing {$status} successfully!");
154    }
155
156    /**
157     * @return array<string, mixed>|null
158     */
159    private function parseJsonFilters(?string $json): ?array
160    {
161        if (empty($json)) {
162            return null;
163        }
164
165        $decoded = json_decode($json, true);
166
167        if (json_last_error() === JSON_ERROR_NONE) {
168            return $decoded;
169        }
170
171        return null;
172    }
173}