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