AppServiceProvider
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->registerInterfaces();
$this->registerServiceProviders();
$this->registerFacadeAliases();
}
/**
* Resister Repositories
*
* @return void
*/
public function registerInterfaces()
{
$this->app->bind(CustomerInterface::class, function (Application $app) {
return new Customer();
});
$this->app->bind(CustomerRepositoryInterface::class, function (Application $app) {
return new CustomerRepository(
$app->make(CustomerFactory::class),
$app->make(CustomerInterface::class)
);
});
$this->app->bind(OrderRepositoryInterface::class, function (Application $app) {
return new OrderRepository(
$app->make(Order::class),
$app->make(OrderDetail::class),
$app->make(OrderDelivery::class)
);
});
}
ドメインモデル
Customer
<?php
declare(strict_types=1);
namespace Oms\Domain\Admin\Customer\CustomerUpdation;
use Carbon\Carbon;
use Oms\Domain\Admin\Customer\PremiumDeadlineValidation\InvalidPremiumDeadlineStatusHigherException;
use Oms\Domain\Admin\Customer\PremiumDeadlineValidation\InvalidPremiumDeadlineStatusLowerException;
use Oms\Domain\Admin\Customer\PremiumDeadlineValidation\PremiumDeadlineValidation;
use Oms\Domain\Admin\Customer\StatusValidation\InvalidStatusLowerException;
use Oms\Domain\Admin\Customer\StatusValidation\StatusValidation;
use Oms\Domain\Front\CustomerMyPage\EditProfile\Entity\FullName;
use Oms\Models\Customer;
use Oms\Models\Interfaces\CustomerInterface;
use Oms\Models\Order;
use Oms\Models\Product;
use Oms\Models\DailyCount;
class CustomerUpdater
{
/** @var CustomerUpdation */
protected $domain;
/** @var PremiumDeadlineValidation */
protected $premiumDeadlineValidation;
/** @var StatusValidation */
protected $statusValidation;
/**
* CustomerUpdater constructor.
*
* @param CustomerUpdation $customerUpdation
* @param PremiumDeadlineValidation $premiumDeadlineValidation
* @param StatusValidation $statusValidation
*/
public function __construct(
CustomerUpdation $customerUpdation,
PremiumDeadlineValidation $premiumDeadlineValidation,
StatusValidation $statusValidation
) {
$this->domain = $customerUpdation;
$this->premiumDeadlineValidation = $premiumDeadlineValidation;
$this->statusValidation = $statusValidation;
}
/**
* @param int $id
* @param array $data
*
* @return CustomerInterface
* @throws InvalidPremiumDeadlineStatusLowerException
* @throws InvalidPremiumDeadlineStatusHigherException
* @throws InvalidStatusLowerException
*/
public function update(int $id, array $data): CustomerInterface
{
$customer = Customer::find($id);
$this->statusValidation->validate((int)$data['status'], $customer);
$this->premiumDeadlineValidation->validate((string)$data['premium_deadline'], (int)$data['status'], $customer);
/**
* Customer Attributes
*/
$customerAttributes = [
'status' => $data['status'],
'premium_deadline' => null,
'email' => strtolower($data['email']),
'name01' => $data['name01'],
'name02' => $data['name02'],
'kana01' => $data['kana01'],
'kana02' => $data['kana02'],
'company' => $data['company'],
'unit' => $data['unit'],
'post' => (int)$data['post'],
'mailmaga_flg' => (bool)$data['mailmaga_flg'],
'job' => (int)$data['job'],
'birth' => null,
'note' => $data['note'],
];
$customerAttributes['name'] = (new FullName($data['name01'], $data['name02']))->getFullName();
if ($data['password']) {
$customerAttributes['password'] = $data['password'];
}
if ($data['premium_deadline']) {
$customerAttributes['premium_deadline'] = $data['premium_deadline'];
}
if ($data['birth']) {
$customerAttributes['birth'] = $data['birth'];
}
if (isset($data['sex'])) {
$customerAttributes['sex'] = (int)$data['sex'];
}
/**
* Address Attributes
*/
$addressAttributes = [
'customer_id' => $id,
'prefecture_id' => null,
'country' => $data['country'] ?? '日本', // dont update on this feature.
'postcode' => $data['postcode'],
'city' => $data['city'],
'line1' => $data['line1'],
'tel' => $data['tel'],
'fax' => $data['fax'],
'sort' => 1, // 会員情報の住所は1
'name01' => $data['name01'],
'name02' => $data['name02'],
'kana01' => $data['kana01'],
'kana02' => $data['kana02'],
];
if ($data['prefecture_id']) {
$addressAttributes['prefecture_id'] = $data['prefecture_id'];
}
/**
* Stripe Customer Attributes
*/
$stripeCustomerAttributes = [
'email' => strtolower($data['email']),
];
/**
* Tags
*/
$articleTags = $data['article_tags'] ?? [];
return $this->domain->update(
$id,
$customerAttributes,
$addressAttributes,
$stripeCustomerAttributes,
$articleTags
);
}
/**
* @param int $orderId
* @param string $autochargeStatus
* @return void
*/
public function updateCustomerStatusByAutochargeStatus(int $orderId, string $autochargeStatus): void
{
$order = Order::find($orderId);
$product = $order->orderDetails()->first()->product;
$customer = $order->customer;
if (is_null($customer)) {
return;
}
$customerStatus = $this->getWillChangedCustomerStatus($autochargeStatus, $product->id, $customer->id);
if (is_null($customerStatus)) {
return;
}
$this->domain->updateCustomerStatus($customer->id, $customerStatus);
// 退会数の記録
// 操作ミスにより誤った有料会員を退会させた場合もカウントされる
DailyCount::todayCountUp('billing_cancel', 1);
}
/**
* @param string $autochargeStatus
* @param int $productId
* @param int $customerId
* @return int|null
*/
public function getWillChangedCustomerStatus(string $autochargeStatus, int $productId, int $customerId): ?int
{
// 定期購買停止でない場合、更新しない
if ($autochargeStatus !== Order::AUTOCHARGE_STATUS_REFUSALED) {
return null;
}
// 購入商品が定期購買商品でない場合、更新しない
$product = Product::find($productId);
if (!$product || !$product->productType->isSubscription()) {
return null;
}
// 顧客データがない(退会済み含む)の場合、更新しない
$customer = Customer::find($customerId);
if (!$customer) {
return null;
}
// 有効な有料会員である場合、更新しない
if ($this->isPremiumCustomer($customer)) {
return null;
}
// customer_statusが変更されない場合、更新しない
$customerStatus = Customer::STATUS_CUSTOMER_PLUS;
if (!$this->domain->willChangedCustomerStatus($customer->id, $customerStatus)) {
return null;
}
return $customerStatus;
}
/**
* @param Customer $customer
* @return bool
*/
private function isPremiumCustomer(Customer $customer): bool
{
// プレミアム会員期限がnull or プレミアム会員期限を過ぎている -> 年間会員ではない
// and 有効な月額継続課金受注が存在しない -> 継続課金会員ではない
if (
(is_null($customer->premium_deadline) || $customer->premium_deadline->lt(Carbon::now()))
&& !$customer->hasAutochargeOngoingOrder()
) {
return false;
}
return true;
}
}
CustomerUpdation
<?php
declare(strict_types=1);
namespace Oms\Domain\Admin\Customer\CustomerUpdation;
use Oms\Domain\Integrations\BlastMail\BlastMailCustomerIntegrationInterface;
use Oms\Domain\Payments\Stripe\Customer\CustomerUpdation as StripeCustomerUpdation;
use Oms\Models\Interfaces\AddressInterface;
use Oms\Models\Interfaces\CustomerInterface;
use Oms\Repositories\Interfaces\AddressRepositoryInterface;
use Oms\Repositories\Interfaces\CustomerRepositoryInterface;
use Stripe\Exception\ApiErrorException as StripeApiErrorException;
class CustomerUpdation
{
/** @var CustomerRepositoryInterface */
protected $customerRepository;
/** @var AddressRepositoryInterface */
protected $addressRepository;
/** @var StripeCustomerUpdation */
protected $stripeCustomerUpdation;
/** @var BlastMailCustomerIntegrationInterface */
protected $blastMailCustomerUpsertion;
public function __construct(
CustomerRepositoryInterface $customerRepository,
AddressRepositoryInterface $addressRepository,
StripeCustomerUpdation $stripeCustomerUpdation,
BlastMailCustomerIntegrationInterface $blastMailCustomerUpsertion
) {
$this->customerRepository = $customerRepository;
$this->addressRepository = $addressRepository;
$this->stripeCustomerUpdation = $stripeCustomerUpdation;
$this->blastMailCustomerUpsertion = $blastMailCustomerUpsertion;
}
public function update(
int $id,
array $customerAttributes,
array $addressAttributes,
array $stripeCustomerAttributes,
array $articleTags = []
): CustomerInterface {
$customer = $this->findCustomer($id);
$this->customerRepository->update($customer, $customerAttributes);
if ($customer->address instanceof AddressInterface) {
$this->addressRepository->update($customer->address, $addressAttributes);
} else {
$this->addressRepository->store($addressAttributes);
}
$customer->articleTags()->sync($articleTags);
$this->stripeCustomerUpdation->update($customer, $stripeCustomerAttributes);
// ブラストメール連携
if (config('blastmail.integration_enable')) {
$this->blastMailCustomerUpsertion->integration($customer);
}
return $this->findCustomer($customer->id);
}
/**
* @param int $customerId
*
* @return CustomerInterface
*/
protected function findCustomer(int $customerId): CustomerInterface
{
return $this->customerRepository->find($customerId)->load(['address', 'articleTags']);
}
/**
* @param int $id
* @param int $status
*
* @return CustomerInterface
*/
public function updateCustomerStatus(int $id, int $status): CustomerInterface
{
$customer = $this->findCustomer($id);
$this->customerRepository->update($customer, ['status' => $status]);
return $this->findCustomer($customer->id);
}
/**
* @param int $id
* @param int $status
*
* @return bool
*/
public function willChangedCustomerStatus(int $id, int $status): bool
{
$customer = $this->findCustomer($id);
return $customer->status !== $status;
}
}
- CustomerUpdaterクラス:
- 顧客情報を更新するためのロジックを提供します。
- 顧客のステータスやプレミアム会員期限などのバリデーションを行います。
- 顧客情報の更新に関連するデータの整形や加工を行います。
- 顧客情報の更新後、ドメインレイヤーのCustomerUpdationクラスを呼び出して実際の更新処理を行います。
- 自動請求ステータスに基づいて顧客のステータスを更新する機能も提供します。
- CustomerUpdationクラス:
- 顧客情報の更新処理を実行します。
- 顧客情報や住所情報、Stripeカスタマー情報などを更新します。
- ブラストメールの連携も行います。
- 顧客情報の検索や更新、ステータスの更新を行うためのメソッドを提供します。
RepositoryInterface
<?php
declare(strict_types=1);
namespace Oms\Repositories\Interfaces;
use Oms\Models\Interfaces\CustomerInterface;
use Traversable;
/**
* Interface CustomerRepositoryInterface
* @package Oms\Repositories\Interfaces
*/
interface CustomerRepositoryInterface
{
/**
* @param int $customerId
*
* @return CustomerInterface
*/
public function find(int $customerId): CustomerInterface;
/**
* @return Traversable
*/
public function findAll(): Traversable;
/**
* @param array $attributes
*
* @return CustomerInterface
*/
public function store(array $attributes): CustomerInterface;
/**
* @param CustomerInterface $customer
* @param array $attributes
*
* @return CustomerInterface
*/
public function update(CustomerInterface $customer, array $attributes): CustomerInterface;
/**
* @param $customer
*
* @return void
*/
public function remove(CustomerInterface $customer);
}
Repository
<?php
declare(strict_types=1);
namespace Oms\Repositories;
use Oms\Models\Customer;
use Oms\Models\Factory\CustomerFactory;
use Oms\Models\Interfaces\CustomerInterface;
use Oms\Repositories\Exceptions\NotExistsCustomerException;
use Oms\Repositories\Interfaces\CustomerRepositoryInterface;
use Traversable;
class CustomerRepository implements CustomerRepositoryInterface
{
/** @var CustomerFactory */
protected $factory;
/** @var Customer */
protected $orm;
/**
* @inheritdoc
*/
public function __construct(CustomerFactory $factory, Customer $customer)
{
$this->factory = $factory;
$this->orm = $customer;
}
/**
* @param int $customerId
*
* @return CustomerInterface
* @throws NotExistsCustomerException
*/
public function find(int $customerId): CustomerInterface
{
$customer = $this->orm->find($customerId);
if (!$customer) {
throw new NotExistsCustomerException();
}
return $customer;
}
/**
* @inheritdoc
*/
public function findAll(): Traversable
{
return $this->orm->get();
}
/**
* @inheritdoc
*/
public function store(array $attributes): CustomerInterface
{
$customer = app(Customer::class);
return $this->factory->persist($customer, $attributes);
}
/**
* @param CustomerInterface $customer
* @param array $attributes
*
* @return CustomerInterface
* @throws NotExistsCustomerException
*/
public function update(CustomerInterface $customer, array $attributes): CustomerInterface
{
if (!$customer->exists) {
throw new NotExistsCustomerException();
}
$updatedCustomer = $this->factory->persist($customer, $attributes);
return $this->find($updatedCustomer->id);
}
/**
* @param CustomerInterface $customer
*
* @return void
* @throws NotExistsCustomerException
*/
public function remove(CustomerInterface $customer)
{
if (!$customer->exists) {
throw new NotExistsCustomerException();
}
$customer->delete();
}
/**
* @param $email
*
* @return \Illuminate\Database\Eloquent\Model|null|static
*/
public function findByEmail($email)
{
$customer = $this->orm->where('email', $email)->first();
if (!$customer) {
throw new NotExistsCustomerException();
}
return $customer;
}
}
Factory
<?php
declare(strict_types=1);
namespace Oms\Models\Factory;
use Oms\Models\Customer;
use Oms\Models\Factory\FactoryInterface;
final class CustomerFactory implements FactoryInterface
{
/** @var Customer */
private $orm;
/**
* CustomerFactory constructor.
*
* @param Customer $customer
*/
public function __construct(Customer $customer)
{
$this->orm = $customer;
}
/**
* @param Customer $customer
* @param array $attributes
*
* @return Customer
*/
public function persist($customer, array $attributes): Customer
{
if (!$customer->exists) {
return $this->create($attributes);
}
return $this->update($customer, $attributes);
}
/**
* @param array $attributes
*
* @return Customer
*/
protected function create(array $attributes): Customer
{
$customer = $this->orm->fill($attributes);
$customer->save();
return $customer->refresh();
}
/**
* @param Customer $customer
* @param array $attributes
*
* @return Customer
*/
protected function update(Customer $customer, array $attributes): Customer
{
$customer->fill($attributes)->update();
return $customer->refresh();
}
}
コメント