장바구니 항목 만들기 | Symfony로 장바구니 만들기


  • Generating entities
  • Generating the OrderItem entity
  • Generating the Order entity
  • Setting Cascade Opeations

  • Migrating the Database
  • Setting the default Order status
  • Avoiding adding duplicate item
  • Removing all items from the Order
  • Calculating the Order totals
  • Creating the Factory



  • 튜토리얼 전체에서 장바구니에 대해 주문에 대해 설명합니다. 아직 주문되지 않은 진행 중인 주문입니다.

    Actually, a cart is an Order in a cart status.



    엔티티 생성

    The Order entity contains:

    • a collection of OrderItem entity: it represents products as its physical copies, with chosen quantities,
    • a status: it will be initialized to cart ,
    • a creation date: the creation date of the order,
    • a modification date: the date the order was last modified.

    OrderItem 엔터티 생성

    Use the Maker bundle to generate the OrderItem entity:

    $ symfony console make:entity OrderItem
    

    Add the fields that we need:

    • product, relation, related to the Product entity, ManyToOne, no, no
    • quantity, integer, no
    <?php
    
    namespace App\Entity;
    
    use App\Repository\OrderItemRepository;
    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * @ORM\Entity(repositoryClass=OrderItemRepository::class)
     */
    class OrderItem
    {
        /**
         * @ORM\Id
         * @ORM\GeneratedValue
         * @ORM\Column(type="integer")
         */
        private $id;
    
        /**
         * @ORM\ManyToOne(targetEntity=Product::class)
         * @ORM\JoinColumn(nullable=false)
         */
        private $product;
    
        /**
         * @ORM\Column(type="integer")
         */
        private $quantity;
    
        public function getId(): ?int
        {
            return $this->id;
        }
    
        public function getProduct(): ?Product
        {
            return $this->product;
        }
    
        public function setProduct(?Product $product): self
        {
            $this->product = $product;
    
            return $this;
        }
    
        public function getQuantity(): ?int
        {
            return $this->quantity;
        }
    
        public function setQuantity(int $quantity): self
        {
            $this->quantity = $quantity;
    
            return $this;
        }
    }
    

    주문 엔터티 생성

    One more time, use Maker bundle to generate the Order entity:

    $ symfony console make:entity Order
    

    Add the fields that we need:

    • items, relation, related to the OrderItem entity, OneToMany, orderRef (order is a reserved word in Mysql), no, yes
    • status, string, 255, no
    • createdAt, datetime, no
    • updatedAt, datetime, no
    <?php
    
    namespace App\Entity;
    
    use App\Repository\OrderRepository;
    use Doctrine\Common\Collections\ArrayCollection;
    use Doctrine\Common\Collections\Collection;
    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * @ORM\Entity(repositoryClass=OrderRepository::class)
     * @ORM\Table(name="`order`")
     */
    class Order
    {
        /**
         * @ORM\Id
         * @ORM\GeneratedValue
         * @ORM\Column(type="integer")
         */
        private $id;
    
        /**
         * @ORM\OneToMany(targetEntity=OrderItem::class, mappedBy="orderRef", orphanRemoval=true)
         */
        private $items;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $status;
    
        /**
         * @ORM\Column(type="datetime")
         */
        private $createdAt;
    
        /**
         * @ORM\Column(type="datetime")
         */
        private $updatedAt;
    
        public function __construct()
        {
            $this->items = new ArrayCollection();
        }
    
        public function getId(): ?int
        {
            return $this->id;
        }
    
        /**
         * @return Collection|OrderItem[]
         */
        public function getItems(): Collection
        {
            return $this->items;
        }
    
        public function addItem(OrderItem $item): self
        {
            if (!$this->items->contains($item)) {
                $this->items[] = $item;
                $item->setOrderRef($this);
            }
    
            return $this;
        }
    
        public function removeItem(OrderItem $item): self
        {
            if ($this->items->removeElement($item)) {
                // set the owning side to null (unless already changed)
                if ($item->getOrderRef() === $this) {
                    $item->setOrderRef(null);
                }
            }
    
            return $this;
        }
    
        public function getStatus(): ?string
        {
            return $this->status;
        }
    
        public function setStatus(string $status): self
        {
            $this->status = $status;
    
            return $this;
        }
    
        public function getCreatedAt(): ?\DateTimeInterface
        {
            return $this->createdAt;
        }
    
        public function setCreatedAt(\DateTimeInterface $createdAt): self
        {
            $this->createdAt = $createdAt;
    
            return $this;
        }
    
        public function getUpdatedAt(): ?\DateTimeInterface
        {
            return $this->updatedAt;
        }
    
        public function setUpdatedAt(\DateTimeInterface $updatedAt): self
        {
            $this->updatedAt = $updatedAt;
    
            return $this;
        }
    
    }
    

    The OrderItem has been updated with the following changes:

    <?php
    
    namespace App\Entity;
    
    use App\Repository\OrderItemRepository;
    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * @ORM\Entity(repositoryClass=OrderItemRepository::class)
     */
    class OrderItem
    {
        // ...
        /**
         * @ORM\ManyToOne(targetEntity=Order::class, inversedBy="items")
         * @ORM\JoinColumn(nullable=false)
         */
        private $orderRef;
        // ...
        public function getOrderRef(): ?Order
        {
            return $this->orderRef;
        }
    
        public function setOrderRef(?Order $orderRef): self
        {
            $this->orderRef = $orderRef;
    
            return $this;
        }
    }
    

    캐스케이드 작업 설정

    When we add a new OrderItem entity to an Order entity, we should persist it before persisting the Order entity. Also, when we will want to remove an Order entity, we should remove all OrderItem entities before removing the Order entity.

    It can be really boring, so we will let Doctrine take care of this internally by using the cascade operations. To do that, add a new cascade option on the items property of the Order entity:

    /**
     * @ORM\OneToMany(targetEntity=OrderItem::class, mappedBy="orderRef", cascade={"persist", "remove"}, orphanRemoval=true)
     */
    private $items;
    

    Therefore, when we will persist/remove an Order entity, Doctrine will automatically call persist/remove on each of the OrderItem objects associated with the Order entity.

    데이터베이스 마이그레이션

    Create a migration file via the Maker bundle:

    $ symfony console make:migration
    

    A migration file has been stored under the migrations/ directory:

    <?php
    
    declare(strict_types=1);
    
    namespace DoctrineMigrations;
    
    use Doctrine\DBAL\Schema\Schema;
    use Doctrine\Migrations\AbstractMigration;
    
    /**
     * Auto-generated Migration: Please modify to your needs!
     */
    final class Version20201215150141 extends AbstractMigration
    {
        public function getDescription() : string
        {
            return '';
        }
    
        public function up(Schema $schema) : void
        {
            // this up() migration is auto-generated, please modify it to your needs
            $this->addSql('CREATE TABLE `order` (id INT AUTO_INCREMENT NOT NULL, status VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
            $this->addSql('CREATE TABLE order_item (id INT AUTO_INCREMENT NOT NULL, product_id INT NOT NULL, order_ref_id INT NOT NULL, quantity INT NOT NULL, INDEX IDX_52EA1F094584665A (product_id), INDEX IDX_52EA1F09E238517C (order_ref_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
            $this->addSql('ALTER TABLE order_item ADD CONSTRAINT FK_52EA1F094584665A FOREIGN KEY (product_id) REFERENCES product (id)');
            $this->addSql('ALTER TABLE order_item ADD CONSTRAINT FK_52EA1F09E238517C FOREIGN KEY (order_ref_id) REFERENCES `order` (id)');
        }
    
        public function down(Schema $schema) : void
        {
            // this down() migration is auto-generated, please modify it to your needs
            $this->addSql('ALTER TABLE order_item DROP FOREIGN KEY FK_52EA1F09E238517C');
            $this->addSql('DROP TABLE `order`');
            $this->addSql('DROP TABLE order_item');
        }
    }
    

    Now, we can update the local database schema:

    $ symfony console doctrine:migrations:migrate
    

    기본 주문 상태 설정

    All new orders must be in the default cart state. In the generated Order entity, initialize the status using a STATUS_CART constant:

    <?php
    
    namespace App\Entity;
    
    // ...
    
    /**
     * @ORM\Entity(repositoryClass=OrderRepository::class)
     * @ORM\Table(name="`order`")
     */
    class Order
    {
        // ...
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $status = self::STATUS_CART;
    
        /**
         * An order that is in progress, not placed yet.
         *
         * @var string
         */
        const STATUS_CART = 'cart';
    
        // ...
    }
    

    중복 주문 항목 추가 방지

    Currently, it's possible to add duplicate OrderItem entities (with the same Product) to the Order entity. Let's fix it.

    Add a equals() method to the OrderItem class to know if the item corresponds to an item given as an argument.

    /**
     * Tests if the given item given corresponds to the same order item.
     *
     * @param OrderItem $item
     *
     * @return bool
     */
    public function equals(OrderItem $item): bool
    {
        return $this->getProduct()->getId() === $item->getProduct()->getId();
    }
    

    Update the addItem() method of the Order entity to sum the quantity if the item already exists:

    public function addItem(OrderItem $item): self
    {
        foreach ($this->getItems() as $existingItem) {
            // The item already exists, update the quantity
            if ($existingItem->equals($item)) {
                $existingItem->setQuantity(
                    $existingItem->getQuantity() + $item->getQuantity()
                );
                return $this;
            }
        }
    
        $this->items[] = $item;
        $item->setOrderRef($this);
    
        return $this;
    }
    

    주문에서 모든 항목 제거

    Add a removeItems() method to the Order class to remove all items from the Order:

    /**
     * Removes all items from the order.
     *
     * @return $this
     */
    public function removeItems(): self
    {
        foreach ($this->getItems() as $item) {
            $this->removeItem($item);
        }
    
        return $this;
    }
    

    주문 합계 계산

    We will need to show the cart summary. Note that we ignore adjustments that order could have such as shipping cost, promo code, etc.

    First, add a getTotal() method to the OrderItem entity to calculate the item total:

    /**
     * Calculates the item total.
     *
     * @return float|int
     */
    public function getTotal(): float
    {
        return $this->getProduct()->getPrice() * $this->getQuantity();
    }
    

    Finally, add a getTotal() method to the Order entity to calculate the order total:

    /**
     * Calculates the order total.
     *
     * @return float
     */
    public function getTotal(): float
    {
        $total = 0;
    
        foreach ($this->getItems() as $item) {
            $total += $item->getTotal();
        }
    
        return $total;
    }
    

    공장 만들기

    The OrderFactory factory will help us to create Order and OrderItem entities with default data. It also allows you to change the Order entity easily.

    <?php
    
    namespace App\Factory;
    
    use App\Entity\Order;
    use App\Entity\OrderItem;
    use App\Entity\Product;
    
    /**
     * Class OrderFactory
     * @package App\Factory
     */
    class OrderFactory
    {
        /**
         * Creates an order.
         *
         * @return Order
         */
        public function create(): Order
        {
            $order = new Order();
            $order
                ->setStatus(Order::STATUS_CART)
                ->setCreatedAt(new \DateTime())
                ->setUpdatedAt(new \DateTime());
    
            return $order;
        }
    
        /**
         * Creates an item for a product.
         *
         * @param Product $product
         *
         * @return OrderItem
         */
        public function createItem(Product $product): OrderItem
        {
            $item = new OrderItem();
            $item->setProduct($product);
            $item->setQuantity(1);
    
            return $item;
        }
    }
    

    Let's go to the next step to manage cart storage.

    좋은 웹페이지 즐겨찾기