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