Laravel オブジェクト指向

AppServiceProvider

/**
     * Resister Repositories
     *
     * @return void
     */
    public function registerInterfaces()
    {
        $this->app->bind(OrderRepositoryInterface::class, function (Application $app) {
            return new OrderRepository(
                $app->make(Order::class),
                $app->make(OrderDetail::class),
                $app->make(OrderDelivery::class)
            );
        });
        $this->app->bind(ReCaptcha::class, function (Application $app) {
            return new ReCaptcha(config('oms.recaptcha.secret_key'));
        });
    }

このコードは、Laravelフレームワークのサービスコンテナに対して、OrderRepositoryInterfaceOrderRepositoryクラスの結びつけ(binding)を定義しています。具体的には、OrderRepositoryInterfaceが要求された場合に、指定されたクロージャ(Closure)が実行され、その結果としてOrderRepositoryの新しいインスタンスが提供されます。

簡単に言えば、これにより、OrderRepositoryInterfaceが要求されると、OrderRepositoryの新しいインスタンスが作成され、その際に必要な依存関係(OrderOrderDetailOrderDelivery)が自動的に解決されます。これにより、アプリケーション内でOrderRepositoryInterfaceを使用する際に、適切な具象クラスが提供されることが保証されます。

Q.OrderRepositoryにOrderRepositoryInterfaceをimplementすればいいのでは?????

OrderRepositoryOrderRepositoryInterfaceを実装している場合、通常はそのインターフェースを用いて依存性注入を行うことができます。しかし、Laravelのサービスコンテナを使用する場合、その仕組みに基づいて依存性を解決することが一般的です。

Laravelのサービスコンテナは、クラスがコンストラクタの型ヒントやメソッドの引数に依存するクラスを自動的に解決します。これにより、開発者は手動で依存性を解決する必要がなくなり、アプリケーションの全体的な柔軟性が向上します。

→リポジトリパターンと呼ぶ

リポジトリパターンは、データベースや永続化ストレージにアクセスする際に、データアクセスロジックを抽象化し、それを利用するクラス(サービスやコントローラなど)に対して依存性注入を行います。これにより、具体的なデータベースの実装を切り替えやすくし、テスト容易性を向上させます。

Laravelでは、Eloquentモデルを使ってデータベースとやり取りすることが一般的ですが、それを隠蔽し、データベース操作に対して特定のクラス(リポジトリ)を介してアクセスすることで、アプリケーションコードがより疎結合になり、テストも容易になります。

そのため、Laravelのサービスコンテナを活用して依存性注入を行い、リポジトリパターンを採用することで、コードの柔軟性と保守性を高めることができます。

Controller

use Illuminate\Session\SessionManager;
use Illuminate\Database\DatabaseManager;
use Oms\Domain\Order\Factory\OrderCreationFactory;

public function __construct(
        SessionManager  $sessionManager,
        DatabaseManager $databaseManager,
        OrderCreationFactory $orderCreationFactory,
    ) {
        $this->sm = $sessionManager;
        $this->dbm  = $databaseManager;
        $this->ocf = $orderCreationFactory;
    }

    /**
     * 受注データ作成
     *
     * @param array $purchase
     * @throws \Exception
     * @return Order
     */
    private function createOrder(array $purchase): Order
    {
        $this->dbm->beginTransaction();
        $customer = $purchase['customer'];
        $cartItems = $purchase['cartItems'];
        // MEMO:カートに入る商品の商品種別が同じことが前提
        $product = $cartItems->first()['product'];
        $paymentType = $purchase['paymentType'];
        try {
            $order = $this->ocf->create($product, $paymentType)
                ->create($customer, $cartItems, $purchase);
            $this->dbm->commit();
            return $order;
        } catch (\Exception $exception) {
            $this->dbm->rollBack();
            throw $exception;
        }
    }

Factory Interface

<?php
declare(strict_types=1);

namespace Oms\Repositories\Interfaces;
use Oms\Models\Order;

interface OrderRepositoryInterface
{
    /**
     * @param array $orderAttributes
     * @param array $detailAttributes
     * @param array $deliveryAttributes
     * @return Order
     */
    public function create(array $orderAttributes, array $detailAttributes, array $deliveryAttributes = []): Order;

    /**
     * @param int $orderId
     * @return bool
     */
    public function delete(int $orderId): bool;
}


<?php

declare(strict_types=1);

namespace Oms\Domain\Order\Factory;

use InvalidArgumentException;
use Oms\Domain\Order\PostFreeOrderCreation;
use Oms\Domain\Order\ProductPrice;
use Oms\Repositories\Interfaces\OrderRepositoryInterface;

class OrderCreationFactory
{
    /**
     * @param Product $product
     * @param PaymentType $paymentType
     * @return OrderCreationInterface
     */
    public function create(Product $product, PaymentType $paymentType): OrderCreationInterface
    {
        /** @var OrderRepositoryInterface $repository */
        $repository = app(OrderRepositoryInterface::class);

        // セミナー・イベント商品
        if ($product->productType->isEvent()) {
        // 記事商品
        } elseif ($product->productType->isPost()) {
            /** @var ProductPrice $productPrice */
            $productPrice = app(ProductPrice::class);
            if ($paymentType->isBankTransfer()) {
                ..........
            }
            if ($paymentType->isCreditCard()) {
                ..........
            }
            if ($paymentType->isFree()) {
                return new PostFreeOrderCreation($repository);
            }
        }

        throw new InvalidArgumentException(
            "Invalid combination of Product->id:$product->id and PaymentType->id:$paymentType->id"
        );
    }
}

Creation(おそらくデータ整形のためのクラス)

<?php

declare(strict_types=1);

namespace Oms\Domain\Order;

use Illuminate\Support\Collection;
use Oms\Domain\Order\Interfaces\OrderCreationInterface;
use Oms\Models\Customer;
use Oms\Models\Order;
use Oms\Repositories\Interfaces\OrderRepositoryInterface;

class PostFreeOrderCreation implements OrderCreationInterface
{
    /** @var OrderRepositoryInterface */
    protected $orderRepository;

    /**
     * @param OrderRepositoryInterface $orderRepository
     */
    public function __construct(OrderRepositoryInterface $orderRepository)
    {
        $this->orderRepository = $orderRepository;
    }

    /**
     * @param Customer $customer
     * @param Collection $cartItems
     * @param array $purchase
     * @return Order
     */
    public function create(Customer $customer, Collection $cartItems, array $purchase): Order
    {
        $orderAttributes = [
            'status' => Order::PAYED,
            'customer_id' => $customer->id,
            'customer_name01' => $customer->name01,
            'customer_name02' => $customer->name02,
            'customer_kana01' => $customer->kana01,
            'customer_kana02' => $customer->kana02,
            'customer_company' => $customer->company,
            'customer_unit' => $customer->unit,
            'customer_post' => $customer->postName,
            'customer_sex' => $customer->sex,
            'customer_email' => $customer->email,
            'customer_tel' => $customer->address->tel,
            'customer_fax' => $customer->address->fax,
            'customer_country' => $customer->address->country,
            'customer_postcode' => $customer->address->postcode,
            'customer_prefecture_name' => $customer->address->prefecture->name,
            'customer_city' => $customer->address->city,
            'customer_line1' => $customer->address->line1,
            'customer_line2' => $customer->address->line2,
            'customer_line3' => $customer->address->line3,
            'note' => $purchase['note'],
            'delivery_fee_total' => 0,
            'delivery_fee_code' => '',
            'tax' => 0,
            'total' => $purchase['total'],
            'payment_type_id' => $purchase['paymentId'],
            'payment_type_name' => $purchase['paymentName'],
        ];
        $detailAttributes = [];
        foreach ($cartItems as $item) {
            $product = $item['product'];
            $post = $item['post'];
            for ($quantity = 0; $quantity < $item['quantity']; $quantity++) {
                $detailAttributes[] = [
                    'product_id' => $product->id,
                    'product_code' => $product->code,
                    'product_name' => $product->name,
                    'product_description' => $product->description,
                    'product_price' => $product->price,
                    'product_type_id' => $product->product_type_id,
                    'tax' => 0,
                    'draft_id' => $post->draft_id,
                ];
            }
        }

        return $this->orderRepository->create($orderAttributes, $detailAttributes);
    }
}

Repository

<?php

declare(strict_types=1);

namespace Oms\Repositories;

use Oms\Models\Order;
use Oms\Models\OrderDelivery;
use Oms\Models\OrderDetail;
use Oms\Repositories\Interfaces\OrderRepositoryInterface;

class OrderRepository implements OrderRepositoryInterface
{
    /** @var Order */
    protected $order;

    /** @var OrderDetail */
    protected $orderDetail;

    /** @var OrderDelivery */
    protected $orderDelivery;

    /**
     * @param Order $order
     * @param OrderDetail $orderDetail
     * @param OrderDelivery $orderDelivery
     */
    public function __construct(Order $order, OrderDetail $orderDetail, OrderDelivery $orderDelivery)
    {
        $this->order = $order;
        $this->orderDetail = $orderDetail;
        $this->orderDelivery = $orderDelivery;
    }

    /**
     * @param array $orderAttributes
     * @param array $detailAttributes
     * @param array $deliveryAttributes
     * @return Order
     */
    public function create(array $orderAttributes, array $detailAttributes, array $deliveryAttributes = []): Order
    {
        /** @var \Oms\Models\Order $order */
        $order = $this->order->newQuery()->create($orderAttributes);
        if (empty($deliveryAttributes)) {
            $orderDeliveryId = null;
        } else {
            /** @var \Oms\Models\OrderDelivery $orderDelivery */
            $orderDelivery = $this->orderDelivery->newQuery()->create($deliveryAttributes);
            $orderDeliveryId = $orderDelivery->id;
        }
        
        foreach ($detailAttributes as $detailAttribute) {
            $this->orderDetail->newQuery()->create(array_merge(
                [
                    'order_id' => $order->id,
                    'order_delivery_id' => $orderDeliveryId,
                ],
                $detailAttribute
            ));
        }

        return $order;
    }

    /**
     * @param int $orderId
     * @return bool
     */
    public function delete(int $orderId): bool
    {
        $order = $this->order->find($orderId);
        if (is_null($order)) {
            return false;
        }

        // boolが返ってくる保証がないのでいったん受ける
        $saved = $order->update([
            'status' => Order::DELETED,
        ]);
        return $saved ? true : false;
    }
}

repositoryはdao

依存注入を用いて関連するテーブルにデータを登録する。

コメント

タイトルとURLをコピーしました