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