ldc-crm/app/Http/Controllers/Bo/CustomerController.php
Jérémy Munsch 23c9efdb22
phpcs
2023-10-06 16:37:31 +02:00

306 lines
12 KiB
PHP

<?php
namespace App\Http\Controllers\Bo;
use App\Enums\CustomerExportType;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\CustomerExport;
use App\Models\Brand;
use App\Models\Customer;
use App\Models\CustomerInfo;
use App\Models\CustomerLogin;
use App\Models\SingleOptin;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use League\Csv\Writer;
use Symfony\Component\HttpFoundation\StreamedResponse;
class CustomerController extends Controller
{
/**
* View all customers.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
$modelTable = (new Customer())->getTable();
$modelClass = Customer::class;
$brands = Brand::query()->select('id', 'name')->get();
$columns = ['customer_infos.lastname', 'customer_infos.firstname'];
// * Jointure pour pouvoir trier les colonnes.
/** @var \Illuminate\Database\Eloquent\Builder */
$query = CustomerInfo::query()->join('customer_logins', function (JoinClause $join) {
$join->on('customer_logins.brand_id', '=', 'customer_infos.brand_id')
->on('customer_logins.customer_id', '=', 'customer_infos.customer_id');
})->join('customers', function (JoinClause $join) {
$join->on('customers.id', '=', 'customer_infos.customer_id');
})->join('brands', function (JoinClause $join) {
$join->on('brands.id', '=', 'customer_infos.brand_id');
});
if ($request->rst) {
Session::remove("crud.$modelTable.brand");
Session::remove("crud.$modelTable.search");
Session::remove("crud.$modelTable.sort_way");
Session::remove("crud.$modelTable.sort_col");
}
if (request()->brand or Session::has("crud.$modelTable.brand")) {
$brand = $request->brand ?? Session::get("crud.$modelTable.brand");
Session::put("crud.$modelTable.brand", $brand);
$query = $query->where('customer_infos.brand_id', $brand);
}
// * Search on crud Label column.
if ($request->search or Session::has("crud.$modelTable.search")) {
$search = $request->search ?? Session::get("crud.$modelTable.search");
Session::put("crud.$modelTable.search", $search);
$query->where(function ($q) use ($columns, $search) {
foreach ($columns as $column) {
$q = $q->orWhere($column, 'LIKE', "%$search%");
}
$q = $q->orWhere('customers.email', 'LIKE', "%$search%");
$q = $q->orWhere('customer_logins.last_connection', 'LIKE', "%$search%");
});
}
// * Sort columns.
if ($request->sort_col) {
Session::put("crud.$modelTable.sort_col", $request->sort_col);
}
if ($request->sort_way) {
Session::put("crud.$modelTable.sort_way", $request->sort_way);
}
if (
Session::has("crud.$modelTable.sort_col") and
Session::has("crud.$modelTable.sort_way") and
(Schema::hasColumn($modelTable, Session::get("crud.$modelTable.sort_col")) or
in_array(Session::get("crud.$modelTable.sort_col"), [
'brands.name',
'customers.email',
'customer_infos.firstname',
'customer_infos.lastname',
'customer_infos.opt_in',
'customer_logins.last_connection',
'customer_logins.status'
]))
) {
$query = $query->orderBy(
Session::get("crud.$modelTable.sort_col"),
Session::get("crud.$modelTable.sort_way")
);
}
$searchFields = trans('Email, Prénom, Nom');
$query->with(['customer', 'brand']);
/** @var \Illuminate\Pagination\AbstractPaginator */
$customerModels = $this->paginate($query);
return view('bo.pages.customers.index', compact(
'modelClass',
'modelTable',
'customerModels',
'brands',
'searchFields'
));
}
/**
* Show a specific custmer.
*
* @param \App\Models\Customer $customer
* @param \App\Models\Brand $brand
* @return \Illuminate\Contracts\View\View
*/
public function show(Customer $customer, Brand $brand): \Illuminate\Contracts\View\View
{
$customerInfos = CustomerInfo::query()->where('customer_id', $customer->id)
->where('brand_id', $brand->id)->firstOrFail();
$customerLogin = CustomerLogin::query()->where('customer_id', $customer->id)
->where('brand_id', $brand->id)->first();
return view('bo.pages.customers.show', compact(
'customerInfos',
'customerLogin'
));
}
/**
* Delete a specific customer info.
*
* @param \App\Models\Customer $customer
* @param \App\Models\Brand $brand
* @return \Illuminate\Http\RedirectResponse
*/
public function delete(Customer $customer, Brand $brand): \Illuminate\Http\RedirectResponse
{
try {
/** @var \App\Models\CustomerInfo */
$customerInfo = $customer->info($brand, Customer::FIRST_OR_FAIL);
/** @var \App\Models\CustomerLogin */
$customerLogin = $customer->login($brand, Customer::FIRST_OR_FAIL);
DB::transaction(function () use (
$brand,
$customer,
$customerLogin,
$customerInfo
) {
if (
$customerInfo->delete() === false ||
$customerLogin->delete() === false
) {
throw new \ErrorException('Api failed to delete customer.');
};
$customer->optInHistory()->whereHas('brand', function ($q) use ($brand) {
$q->where('id', $brand->id);
})->delete();
$logins = $customer->logins()->count();
$infos = $customer->infos()->count();
$history = $customer->optInHistory()->count();
// If customer is not associated with any brand then delete.
if (!$logins and !$infos and !$history) {
if ($customer->delete() === false) {
throw new \ErrorException('Api failed to update customer.');
}
}
});
} catch (\ErrorException $e) {
return redirect()->route('bo.customers.index')
->with('error', trans('Le compte n\'a pas pu être supprimé.'));
} //end try
return redirect()->route('bo.customers.index')->with('warning', trans('Compte supprimé.'));
}
/**
* Show export the customers form.
*
* @return \Illuminate\Contracts\View\View
*/
public function export(): \Illuminate\Contracts\View\View
{
$brands = Brand::all();
return view('bo.pages.customers.export', compact(
'brands'
));
}
/**
* Exports customers following the requested export type.
*
* @param \App\Http\Requests\Bo\CustomerExport $request
* @return \Symfony\Component\HttpFoundation\StreamedResponse
* @throws \Exception If the request export type in not handled.
*/
public function exportSubmit(CustomerExport $request): \Symfony\Component\HttpFoundation\StreamedResponse
{
$pagination = 2000;
$header = '"lastname";"firstname";"email";"hashcode"';
$w = Writer::createFromString($header . PHP_EOL);
$w->setDelimiter(';');
$w->setEnclosure('"');
$w->setOutputBOM(Writer::BOM_UTF8);
$exportType = CustomerExportType::make(\intval($request->export_type));
// * We send the CSV using Symfony StreamedResponse.
$response = new StreamedResponse();
$response->headers->set('Content-Encoding', 'none');
$response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
$response->headers->set('Description', 'File Transfer');
$response->headers->set(
'Content-Disposition',
sprintf(
'attachment; filename="Export pour newsletter %s %s.csv"',
Carbon::now()->isoFormat('DD-MM-YYYY_HH-mm'),
Str::slug($exportType->label())
)
);
switch ($exportType) {
case CustomerExportType::both:
$continue = true;
// ! Continue
case CustomerExportType::customer:
$page = 1;
/** @var \Illuminate\Database\Eloquent\Builder $req */
$req = CustomerInfo::query()->where('brand_id', $request->brand)
->where('opt_in', true);
$total = $req->count();
$pages = \ceil($total / $pagination);
do {
/** @var \Illuminate\Pagination\LengthAwarePaginator $items */
$items = $req->with('customer', function ($q) {
$q->select(['id', 'email']);
})->paginate(
$pagination,
['lastname', 'firstname', 'hashcode', 'customer_id'],
'page',
$page
);
$items->map(function (CustomerInfo $customerInfo) use ($w) {
$w->insertOne([
'lastname' => $customerInfo->lastname,
'firstname' => $customerInfo->firstname,
'email' => $customerInfo->customer->email,
'hashcode' => $customerInfo->hashcode
]);
});
} while ($page++ <= $pages);
if (!isset($continue)) {
break;
}
// ! Continue
case CustomerExportType::singleOptin:
SingleOptin::query()->whereHas('brand', function (Builder $q) use ($request) {
$q->where('id', $request->brand);
})->get()->map(function (SingleOptin $singleOptin) use ($w) {
$w->insertOne([
'lastname' => '',
'firstname' => '',
'email' => $singleOptin->email,
'hashcode' => $singleOptin->hashcode
]);
});
break;
default:
throw new \Exception(sprintf('Unhandled CustomerExportType %s', $exportType->label()));
} //end switch
/*
We create a callable to output the CSV in chunk
with Symfony StreamResponse you can flush the body content if necessary
see Symfony documentation for more information
*/
// * The flush value should depend on your CSV size.
$flush_threshold = 1000;
$response->setCallback(function () use ($w, $flush_threshold) {
foreach ($w->chunk(1024) as $offset => $chunk) {
echo $chunk;
if ($offset % $flush_threshold === 0) {
flush();
}
}
});
return $response;
}
}