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