Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerController
n/a
0 / 0
n/a
0 / 0
31
n/a
0 / 0
 index
n/a
0 / 0
n/a
0 / 0
13
 show
n/a
0 / 0
n/a
0 / 0
1
 delete
n/a
0 / 0
n/a
0 / 0
8
 export
n/a
0 / 0
n/a
0 / 0
1
 exportSubmit
n/a
0 / 0
n/a
0 / 0
8
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Enums\CustomerExportType;
6use App\Http\Requests\CustomerExport;
7use App\Models\Brand;
8use App\Models\Customer;
9use App\Models\CustomerInfo;
10use App\Models\CustomerLogin;
11use App\Models\SingleOptin;
12use Illuminate\Database\Eloquent\Builder;
13use Illuminate\Database\Query\JoinClause;
14use Illuminate\Http\Request;
15use Illuminate\Support\Carbon;
16use Illuminate\Support\Facades\DB;
17use Illuminate\Support\Facades\Schema;
18use Illuminate\Support\Facades\Session;
19use Illuminate\Support\Str;
20use League\Csv\Writer;
21use Symfony\Component\HttpFoundation\StreamedResponse;
22
23/**
24 * @codeCoverageIgnore
25 */
26class CustomerController extends Controller
27{
28    /**
29     * View all customers
30     *
31     * @param \Illuminate\Http\Request $request
32     * @return \Illuminate\Http\Response
33     */
34    public function index(Request $request)
35    {
36        $modelTable = (new Customer())->getTable();
37        $modelClass = Customer::class;
38        $brands     = Brand::select('id', 'name')->get();
39        $columns    = ['customer_infos.lastname', 'customer_infos.firstname'];
40
41        // * Jointure pour pouvoir trier les colonnes.
42        /** @var \Illuminate\Database\Eloquent\Builder */
43        $query = CustomerInfo::join('customer_logins', function (JoinClause $join) {
44            $join->on('customer_logins.brand_id', '=', 'customer_infos.brand_id')
45            ->on('customer_logins.customer_id', '=', 'customer_infos.customer_id');
46        })
47        ->join('customers', function (JoinClause $join) {
48            $join->on('customers.id', '=', 'customer_infos.customer_id');
49        })
50        ->join('brands', function (JoinClause $join) {
51            $join->on('brands.id', '=', 'customer_infos.brand_id');
52        });
53
54        if ($request->rst) {
55            Session::remove("crud.$modelTable.brand");
56            Session::remove("crud.$modelTable.search");
57            Session::remove("crud.$modelTable.sort_way");
58            Session::remove("crud.$modelTable.sort_col");
59        }
60
61        if (request()->brand or Session::has("crud.$modelTable.brand")) {
62            $brand = $request->brand ?? Session::get("crud.$modelTable.brand");
63            Session::put("crud.$modelTable.brand", $brand);
64            $query = $query->where('customer_infos.brand_id', $brand);
65        }
66
67        // * Search on crud Label column.
68        if ($request->search or Session::has("crud.$modelTable.search")) {
69            $search = $request->search ?? Session::get("crud.$modelTable.search");
70            Session::put("crud.$modelTable.search", $search);
71            $query->where(function ($q) use ($columns, $search) {
72                foreach ($columns as $column) {
73                    $q = $q->orWhere($column, 'LIKE', "%$search%");
74                }
75                $q = $q->orWhere('customers.email', 'LIKE', "%$search%");
76                $q = $q->orWhere('customer_logins.last_connection', 'LIKE', "%$search%");
77            });
78        }
79
80        // * Sort columns.
81        if ($request->sort_col) {
82            Session::put("crud.$modelTable.sort_col", $request->sort_col);
83        }
84        if ($request->sort_way) {
85            Session::put("crud.$modelTable.sort_way", $request->sort_way);
86        }
87
88        if (
89            Session::has("crud.$modelTable.sort_col") and
90            Session::has("crud.$modelTable.sort_way") and
91            (Schema::hasColumn($modelTable, Session::get("crud.$modelTable.sort_col")) or
92            in_array(Session::get("crud.$modelTable.sort_col"), [
93                'brands.name',
94                'customers.email',
95                'customer_infos.firstname',
96                'customer_infos.lastname',
97                'customer_infos.opt_in',
98                'customer_logins.last_connection',
99                'customer_logins.status'
100            ]))
101        ) {
102            $query = $query->orderBy(
103                Session::get("crud.$modelTable.sort_col"),
104                Session::get("crud.$modelTable.sort_way")
105            );
106        }
107
108        $searchFields = trans('Email, Prénom, Nom');
109
110        /** @var \Illuminate\Pagination\AbstractPaginator */
111        $customerModels = $query->paginate(50);
112
113        return view('customers.index', compact(
114            'modelClass',
115            'modelTable',
116            'customerModels',
117            'brands',
118            'searchFields'
119        ));
120    }
121
122    /**
123     * Show a specific custmer
124     *
125     * @param \App\Models\Customer $customer
126     * @param \App\Models\Brand    $brand
127     * @return \Illuminate\Http\Response
128     */
129    public function show(Customer $customer, Brand $brand)
130    {
131        $customerInfos = CustomerInfo::where('customer_id', $customer->id)
132            ->where('brand_id', $brand->id)->firstOrFail();
133        $customerLogin = CustomerLogin::where('customer_id', $customer->id)
134            ->where('brand_id', $brand->id)->first();
135
136        return view('customers.show', compact(
137            'customerInfos',
138            'customerLogin'
139        ));
140    }
141
142    /**
143     * Delete a specific customer info
144     *
145     * @param \App\Models\Customer $customer
146     * @param \App\Models\Brand    $brand
147     * @return \Illuminate\Http\RedirectResponse
148     */
149    public function delete(Customer $customer, Brand $brand)
150    {
151        try {
152            // TODO: Gestion avec mesBR.
153            /** @var \App\Models\CustomerInfo */
154            $customerInfo = $customer->info($brand, Customer::FIRST_OR_FAIL);
155            /** @var \App\Models\CustomerLogin */
156            $customerLogin = $customer->login($brand, Customer::FIRST_OR_FAIL);
157
158            DB::transaction(function () use (
159                $brand,
160                $customer,
161                $customerLogin,
162                $customerInfo
163            ) {
164                if (
165                    $customerInfo->delete() === false ||
166                    $customerLogin->delete() === false
167                ) {
168                    throw new \ErrorException('Api failed to delete customer.');
169                };
170
171                $customer->optInHistory()->whereHas('brand', function ($q) use ($brand) {
172                    $q->where('id', $brand->id);
173                })->delete();
174
175                $logins  = $customer->logins()->count();
176                $infos   = $customer->infos()->count();
177                $history = $customer->optInHistory()->count();
178
179                // If customer is not associated with any brand then delete.
180                if (!$logins and !$infos and !$history) {
181                    if ($customer->delete() === false) {
182                        throw new \ErrorException('Api failed to update customer.');
183                    }
184                }
185            });
186        } catch (\ErrorException $e) {
187            return redirect()->route('customers.index')
188            ->with('error', trans('Le compte n\'a pas pu être supprimé.'));
189        } //end try
190
191        return redirect()->route('customers.index')->with('warning', trans('Compte supprimé.'));
192    }
193
194    /**
195     * Show export the customers form
196     *
197     * @return \Illuminate\Http\Response
198     */
199    public function export()
200    {
201        $brands = Brand::all();
202        return view('customers.export', compact(
203            'brands'
204        ));
205    }
206
207    /**
208     * Exports customers following the requested export type
209     *
210     * @param \App\Http\Requests\CustomerExport $request
211     * @return \Symfony\Component\HttpFoundation\StreamedResponse
212     * @throws \Exception If the request export type in not handled.
213     */
214    public function exportSubmit(CustomerExport $request)
215    {
216        $pagination = 2000;
217        $header     = '"lastname";"firstname";"email";"hashcode"';
218
219        $w = Writer::createFromString($header . PHP_EOL);
220        $w->setDelimiter(';');
221        $w->setEnclosure('"');
222        $w->setOutputBOM(Writer::BOM_UTF8);
223
224        $exportType = CustomerExportType::make(\intval($request->export_type));
225
226        // * We send the CSV using Symfony StreamedResponse.
227        $response = new StreamedResponse();
228        $response->headers->set('Content-Encoding', 'none');
229        $response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
230        $response->headers->set('Description', 'File Transfer');
231        $response->headers->set(
232            'Content-Disposition',
233            sprintf(
234                'attachment; filename="Export pour newsletter %s %s.csv"',
235                Carbon::now()->format('d-m-Y_H-i'),
236                Str::slug($exportType)
237            )
238        );
239
240        switch ($exportType) {
241            case CustomerExportType::both():
242                $continue = true;
243                // ! Continue
244            case CustomerExportType::customer():
245                $page = 1;
246                /** @var \Illuminate\Database\Eloquent\Builder $req */
247                $req   = CustomerInfo::where('brand_id', $request->brand)
248                ->where('opt_in', true);
249                $total = $req->count();
250                $pages = \ceil($total / $pagination);
251                do {
252                    /** @var \Illuminate\Pagination\LengthAwarePaginator $items */
253                    $items = $req->with('customer', function ($q) {
254                        $q->select(['id', 'email']);
255                    })->paginate(
256                        $pagination,
257                        ['lastname', 'firstname', 'hashcode', 'customer_id'],
258                        'page',
259                        $page
260                    );
261                    $items->map(function (CustomerInfo $customerInfo) use ($w) {
262                        $w->insertOne([
263                            'lastname' => $customerInfo->lastname,
264                            'firstname' => $customerInfo->firstname,
265                            'email' => $customerInfo->customer->email,
266                            'hashcode' => $customerInfo->hashcode
267                        ]);
268                    });
269                } while ($page++ <= $pages);
270                if (!isset($continue)) {
271                    break;
272                }
273                // ! Continue
274            case CustomerExportType::singleOptin():
275                SingleOptin::query()->whereHas('brand', function (Builder $q) use ($request) {
276                    $q->where('id', $request->brand);
277                })->get()->map(function (SingleOptin $singleOptin) use ($w) {
278                    $w->insertOne([
279                        'lastname' => '',
280                        'firstname' => '',
281                        'email' => $singleOptin->email,
282                        'hashcode' => $singleOptin->hashcode
283                    ]);
284                });
285                break;
286            default:
287                throw new \Exception(sprintf('Unhandled CustomerExportType %s', $exportType->label));
288        } //end switch
289
290        /*
291            We create a callable to output the CSV in chunk
292            with Symfony StreamResponse you can flush the body content if necessary
293            see Symfony documentation for more information
294        */
295
296        // * The flush value should depend on your CSV size.
297        $flush_threshold = 1000;
298
299        $response->setCallback(function () use ($w, $flush_threshold) {
300            foreach ($w->chunk(1024) as $offset => $chunk) {
301                echo $chunk;
302                if ($offset % $flush_threshold === 0) {
303                    flush();
304                }
305            }
306        });
307        return $response;
308    }
309}