Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 128
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
QuestionController
0.00% covered (danger)
0.00%
0 / 128
0.00% covered (danger)
0.00%
0 / 9
462
0.00% covered (danger)
0.00%
0 / 1
 index
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
 create
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 store
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
6
 edit
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 update
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
6
 show
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 destroy
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 export
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 exportSubmit
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 1
72
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Http\Controllers\Controller;
6use App\Http\Requests\QuestionExport;
7use App\Http\Requests\Questions\StoreQuestionRequest;
8use App\Http\Requests\Questions\UpdateQuestionRequest;
9use App\Models\Brand;
10use App\Models\Question;
11use Carbon\Carbon;
12use Illuminate\Http\Request;
13use Illuminate\Support\Facades\DB;
14use Illuminate\Support\Str;
15use League\Csv\Writer;
16use Symfony\Component\HttpFoundation\StreamedResponse;
17
18class QuestionController extends Controller
19{
20    /**
21     * Display a listing of the resource.
22     *
23     * @param \Illuminate\Http\Request $request
24     * @return \Illuminate\Contracts\View\View
25     */
26    public function index(Request $request): \Illuminate\Contracts\View\View
27    {
28        /** @var \Illuminate\Database\Eloquent\Builder $query */
29        $query = Question::query();
30
31        if ($request->brand) {
32            $query = $query->where('brand_id', '=', $request->brand);
33        }
34        if ($request->search) {
35            $this->searchQuery(
36                $query,
37                $request->search,
38                null,
39                'question',
40            );
41        }
42
43        $this->sortQuery($query);
44        $searchFields = trans('Question');
45
46        /** @var \Illuminate\Pagination\AbstractPaginator */
47        $questionModels = $query->paginate(12);
48
49        return view('questions.index', compact('questionModels', 'searchFields'));
50    }
51
52    /**
53     * Show the form for creating a new resource.
54     *
55     * @param \App\Models\Question $question
56     * @return \Illuminate\Contracts\View\View
57     */
58    public function create(Question $question): \Illuminate\Contracts\View\View
59    {
60        $brandModels = Brand::all();
61
62        return view('questions.create', [
63            'questionModel' => $question,
64            'brandModels' => $brandModels
65        ]);
66    }
67
68    /**
69     * Store a newly created resource in storage.
70     *
71     * @param \App\Http\Requests\Questions\StoreQuestionRequest $request
72     * @return \Illuminate\Http\RedirectResponse
73     */
74    public function store(StoreQuestionRequest $request): \Illuminate\Http\RedirectResponse
75    {
76        return DB::transaction(function () use ($request) {
77            $question = new Question();
78            $question->fill($request->validated());
79            if ($question->saveOrFail()) {
80                return redirect()
81                    ->route('questions.edit', $question)
82                    ->with('success', trans(
83                        ':model a été créé.',
84                        ['model' => Str::singular(trans('models.classes.questions'))]
85                    ));
86            }
87            return redirect()
88                ->back()
89                ->with('error', trans(
90                    ':model n\'a pas été créé.',
91                    ['model' => Str::singular(trans('models.classes.questions'))]
92                ));
93        });
94    }
95
96    /**
97     * Show the form for editing the specified resource.
98     *
99     * @param \App\Models\Question $question
100     * @return \Illuminate\Contracts\View\View
101     */
102    public function edit(Question $question): \Illuminate\Contracts\View\View
103    {
104        $brandModels = Brand::all();
105
106        return view('questions.edit', [
107            'questionModel' => $question,
108            'brandModels' => $brandModels
109        ]);
110    }
111
112    /**
113     * Update a spacific resource in storage.
114     *
115     * @param \App\Http\Requests\Questions\UpdateQuestionRequest $request
116     * @param \App\Models\Question                               $question
117     * @return \Illuminate\Http\RedirectResponse
118     */
119    public function update(UpdateQuestionRequest $request, Question $question): \Illuminate\Http\RedirectResponse
120    {
121        return DB::transaction(function () use ($request, $question) {
122            $question->fill($request->validated());
123            if ($question->saveOrFail()) {
124                return redirect()
125                    ->route('questions.edit', $question)
126                    ->with('success', trans(
127                        ':model a été mis à jour.',
128                        ['model' => Str::singular(trans('models.classes.questions'))]
129                    ));
130            }
131            return redirect()
132                ->back()
133                ->with('error', trans(
134                    ':model n\'a pas été mis à jour.',
135                    ['model' => Str::singular(trans('models.classes.questions'))]
136                ));
137        });
138    }
139
140    /**
141     * Show the specified resource.
142     *
143     * @param \App\Models\Question $question
144     * @return \Illuminate\Contracts\View\View
145     */
146    public function show(Question $question): \Illuminate\Contracts\View\View
147    {
148        return view('questions.show', [
149            'questionModel' => $question
150        ]);
151    }
152
153    /**
154     * Remove the specified resource from storage.
155     *
156     * @param \App\Models\Question $question
157     * @return \Illuminate\Http\RedirectResponse
158     */
159    public function destroy(Question $question): \Illuminate\Http\RedirectResponse
160    {
161        if ($question->deleteOrFail()) {
162            return redirect()->route('questions.index')
163                ->with('success', trans(
164                    ':model a été supprimé.',
165                    ['model' => Str::singular(trans('models.classes.questions'))]
166                ));
167        }
168        return redirect()->back()
169            ->with('error', trans(
170                ':model n\'a pas pu être supprimé.',
171                ['model' => Str::singular(trans('models.classes.questions'))]
172            ));
173    }
174
175    /**
176     * Shows the question export form
177     *
178     * @return \Illuminate\Contracts\View\View
179     */
180    public function export(): \Illuminate\Contracts\View\View
181    {
182        $brands = Brand::all();
183        return view('questions.export', compact('brands'));
184    }
185
186    /**
187     * Exports questions as csv the requested export type
188     *
189     * @param \App\Http\Requests\QuestionExport $request
190     * @return \Symfony\Component\HttpFoundation\StreamedResponse
191     */
192    public function exportSubmit(QuestionExport $request): \Symfony\Component\HttpFoundation\StreamedResponse
193    {
194        $results = [];
195        /** @var \App\Models\Brand $brand */
196        $brand     = Brand::query()->where('id', $request->brand)->firstOrFail();
197        $questions = $brand->questions->load('answers');
198
199        foreach ($questions as $question) {
200            $answersCounts = DB::query()
201                ->selectRaw('count(*) as total, answer_id')
202                ->from('customer_answer')
203                ->whereIn('answer_id', $question->answers->pluck('id'))
204                ->groupBy('answer_id')
205                ->get()->keyBy('answer_id');
206
207            $results[$question->id] = [
208                'question' => $question->question,
209                'answers' => []
210            ];
211            foreach ($question->answers as $answer) {
212                $results[$question->id]['answers'][] =
213                    [
214                        "$answer->answer ",
215                        $answersCounts[$answer->id]->total ?? 0
216                    ];
217            }
218        }
219
220        $w = Writer::createFromString('');
221        $w->setDelimiter(';');
222        $w->setEnclosure('"');
223        $w->setOutputBOM(Writer::BOM_UTF8);
224
225        // * We send the CSV using Symfony StreamedResponse.
226        $response = new StreamedResponse();
227        $response->headers->set('Content-Encoding', 'none');
228        $response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
229        $response->headers->set('Description', 'File Transfer');
230        $response->headers->set(
231            'Content-Disposition',
232            sprintf(
233                'attachment; filename="Export pour newsletter %s %s.csv"',
234                Carbon::now()->isoFormat('DD-MM-YYYY_HH-mm'),
235                Str::slug($brand->name)
236            )
237        );
238
239        foreach ($results as $result) {
240            $w->insertOne([
241                'question' => $result['question'],
242            ]);
243            foreach ($result['answers'] as $answer) {
244                $w->insertOne($answer);
245            }
246            $w->insertOne([]);
247        }
248
249        // * Push empty line if nothing to export
250        if (empty($results)) {
251            $w->insertOne([
252                'Aucune question pour cette marque'
253            ]);
254        }
255
256        /*
257            We create a callable to output the CSV in chunk
258            with Symfony StreamResponse you can flush the body content if necessary
259            see Symfony documentation for more information
260        */
261
262        // * The flush value should depend on your CSV size.
263        $flush_threshold = 1000;
264
265        $response->setCallback(function () use ($w, $flush_threshold) {
266            foreach ($w->chunk(1024) as $offset => $chunk) {
267                echo $chunk;
268                if ($offset % $flush_threshold === 0) {
269                    flush();
270                }
271            }
272        });
273        return $response;
274    }
275}