generated from boboko/starter
157 lines
6.0 KiB
PHP
157 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Account;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Validation\Rule;
|
|
use Illuminate\View\View;
|
|
use Lunar\Models\Country;
|
|
use Lunar\Models\State;
|
|
use Modules\Core\Cart\Services\CartService;
|
|
use Modules\Core\Checkout\Services\CheckoutService;
|
|
use Modules\Core\Customer\Services\CustomerAccountService;
|
|
use Modules\Core\Privacy\Services\PrivacyService;
|
|
|
|
/**
|
|
* The profile page: name, invoice details, the one address, and account
|
|
* deletion. The customer keeps a single address, flagged as both the
|
|
* shipping and billing default, so allowing several later is a UI change
|
|
* only (the data is already Lunar's own addresses table).
|
|
*/
|
|
class AccountController extends Controller
|
|
{
|
|
// Single-country store, same as CheckoutController::STORE_COUNTRY_ISO3.
|
|
private const STORE_COUNTRY_ISO3 = 'GRC';
|
|
|
|
private const ADDRESS_FIELDS = ['line_one', 'city', 'postcode', 'state', 'contact_phone'];
|
|
|
|
public function __construct(
|
|
private readonly CustomerAccountService $account,
|
|
) {}
|
|
|
|
public function show(string $locale, Request $request): View
|
|
{
|
|
$user = $request->user();
|
|
$customer = $this->account->customer($user);
|
|
|
|
return view('account.show', [
|
|
'user' => $user,
|
|
'customer' => $customer,
|
|
'address' => $this->defaultAddress($user),
|
|
'regions' => State::where('country_id', $this->storeCountry()->id)->orderBy('name')->get(['id', 'name']),
|
|
]);
|
|
}
|
|
|
|
public function update(string $locale, Request $request): RedirectResponse
|
|
{
|
|
$user = $request->user();
|
|
$country = $this->storeCountry();
|
|
|
|
// The address is all-or-nothing: typing any part of it makes the rest
|
|
// (and the name, which Lunar requires on every address) required.
|
|
$anyAddressField = implode(',', self::ADDRESS_FIELDS);
|
|
|
|
$data = $request->validate([
|
|
'first_name' => ['nullable', 'string', 'max:255', 'required_with:'.$anyAddressField],
|
|
'last_name' => ['nullable', 'string', 'max:255', 'required_with:'.$anyAddressField],
|
|
'invoice' => ['boolean'],
|
|
'company_name' => ['nullable', 'string', 'max:255', 'required_if_accepted:invoice'],
|
|
'tax_identifier' => ['nullable', 'digits:9', 'required_if_accepted:invoice'],
|
|
'line_one' => ['nullable', 'string', 'max:255', 'required_with:'.$anyAddressField],
|
|
'city' => ['nullable', 'string', 'max:255', 'required_with:'.$anyAddressField],
|
|
'postcode' => ['nullable', 'regex:/^\d{3}\s?\d{2}$/', 'required_with:'.$anyAddressField],
|
|
'state' => [
|
|
'nullable',
|
|
'string',
|
|
'required_with:'.$anyAddressField,
|
|
Rule::exists((new State)->getTable(), 'name')->where('country_id', $country->id),
|
|
],
|
|
'contact_phone' => ['nullable', 'string', 'max:30'],
|
|
'recovery_consent' => ['boolean'],
|
|
]);
|
|
|
|
$invoice = $request->boolean('invoice');
|
|
|
|
$this->account->updateProfile($user, [
|
|
'first_name' => $data['first_name'] ?? null,
|
|
'last_name' => $data['last_name'] ?? null,
|
|
'company_name' => $invoice ? $data['company_name'] : null,
|
|
'tax_identifier' => $invoice ? $data['tax_identifier'] : null,
|
|
]);
|
|
|
|
if (filled($data['line_one'] ?? null)) {
|
|
$addressData = [
|
|
...collect($data)->only(self::ADDRESS_FIELDS)->all(),
|
|
'first_name' => $data['first_name'],
|
|
'last_name' => $data['last_name'],
|
|
'country_id' => $country->id,
|
|
'contact_email' => $user->email,
|
|
'shipping_default' => true,
|
|
'billing_default' => true,
|
|
];
|
|
|
|
$existing = $this->defaultAddress($user);
|
|
|
|
$existing
|
|
? $this->account->updateAddress($user, $existing->id, $addressData)
|
|
: $this->account->createAddress($user, $addressData);
|
|
}
|
|
|
|
$this->updateRecoveryConsent($user, $request->boolean('recovery_consent'));
|
|
|
|
return redirect()->route('account')->with('status', __('storefront.account.saved'));
|
|
}
|
|
|
|
/**
|
|
* "Email me a reminder if I don't finish my order", as a standing
|
|
* choice — stored on the customer via boboko-core's
|
|
* CustomerAccountService::setRecoveryConsent(), and applied to the
|
|
* current cart too, so opting out stops reminders for it right away.
|
|
*/
|
|
private function updateRecoveryConsent($user, bool $consent): void
|
|
{
|
|
$this->account->setRecoveryConsent($user, $consent);
|
|
|
|
// Only an existing cart; never create one just to record this.
|
|
$cart = app(CartService::class)->current();
|
|
|
|
if ($cart && (bool) data_get($cart, 'meta.recovery_consent') !== $consent) {
|
|
app(CheckoutService::class)->setRecoveryConsent($consent);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Self-service deletion: opens core's 30-day grace-period erasure request
|
|
* (which blocks the login right away) and logs out. Logging back in within
|
|
* the grace period cancels it; see core's docs/privacy.md.
|
|
*/
|
|
public function destroy(string $locale, Request $request, PrivacyService $privacy): RedirectResponse
|
|
{
|
|
$user = $request->user();
|
|
|
|
$privacy->requestErasureForUser($user, $user);
|
|
|
|
Auth::logout();
|
|
|
|
$request->session()->invalidate();
|
|
$request->session()->regenerateToken();
|
|
|
|
return redirect()->route('login')->with('status', __('storefront.account.deletion_requested'));
|
|
}
|
|
|
|
private function defaultAddress($user)
|
|
{
|
|
$addresses = collect($this->account->addresses($user));
|
|
|
|
return $addresses->firstWhere('shipping_default', true) ?? $addresses->first();
|
|
}
|
|
|
|
private function storeCountry(): Country
|
|
{
|
|
return Country::where('iso3', self::STORE_COUNTRY_ISO3)->firstOrFail();
|
|
}
|
|
}
|