Doctrine - 自定义列类型未更新

Doctrine - custom column type not getting updated

我有一个具有自定义列类型 stock_collection 的实体 Dish

<?php

declare(strict_types=1);

namespace App\Entity\Dish;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Resource\Model\ResourceInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Webmozart\Assert\Assert as WebmozartAssert;

/**
 * @ORM\Entity
 */
class Dish implements ResourceInterface
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    private ?int $id = null;

    /**
     * @ORM\Column(type="string", length=20, unique=true, nullable=false)
     *
     * @Assert\Length(min="3", max="20")
     * @Assert\NotBlank
     */
    private string $code;

    /**
     * @var Collection<int,Stock>
     *
     * @Assert\Valid
     *
     * @ORM\Column(type="stock_collection")
     */
    private Collection $stocks;

    public function __construct($code, $stock) 
    {
        $this->code = $code;

        $this->stocks = new ArrayCollection(
            array_map(
                fn (int $quantity, string $code): Stock => new Stock($code, $quantity),
                $stock,
                array_keys($stock)
            )
        );
    }

    public function getCode(): string
    {
        return $this->code;
    }

    public function setCode(string $code): void
    {
        $this->code = $code;
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getStocks(): Collection
    {
        return $this->stocks;
    }

    public function setStocks(Collection $stocks): void
    {
        $this->stocks = $stocks;
    }

    public function decreaseStockQuantity(string $menuType, int $quantity): void
    {
        WebmozartAssert::greaterThanEq($quantity, 0, 'Can not decrease of a negative quantity');

        $this->getStock($menuType)->decreaseQuantity($quantity);
    }

    public function getStockQuantity(string $menuType): int
    {
        return $this->getStock($menuType)->getQuantity();
    }

    public function getStock(string $menuType): Stock
    {
        foreach ($this->stocks as $stock) {
            if ($stock->getMenuType() === $menuType) {
                return $stock;
            }
        }

        throw new \InvalidArgumentException(sprintf('Stock for menu type %s not found', $menuType));
    }
}
<?php

declare(strict_types=1);

namespace App\Entity\Dish;

use Symfony\Component\Validator\Constraints as Assert;
use Webmozart\Assert\Assert as WebmozartAssert;

class Stock implements \JsonSerializable
{
    /**
     * @Assert\NotBlank
     */
    private string $menuType;

    /**
     * @Assert\NotBlank
     * @Assert\PositiveOrZero
     */
    private int $quantity;

    private bool $inventory;

    public function __construct(string $menuType, int $quantity = 0, bool $inventory = true)
    {
        $this->menuType = $menuType;
        $this->quantity = $quantity;
        $this->inventory = $inventory;
    }

    public static function fromArray(array $data): self
    {
        WebmozartAssert::keyExists($data, 'menuType');
        WebmozartAssert::keyExists($data, 'quantity');
        WebmozartAssert::keyExists($data, 'inventory');

        return new self(
            $data['menuType'],
            $data['quantity'],
            $data['inventory']
        );
    }

    public function getMenuType(): string
    {
        return $this->menuType;
    }

    public function setMenuType(string $menuType): void
    {
        $this->menuType = $menuType;
    }

    public function getQuantity(): int
    {
        return $this->quantity;
    }

    public function setQuantity(int $quantity): void
    {
        $this->quantity = $quantity;
    }

    public function inventory(): bool
    {
        return $this->inventory;
    }

    public function setInventory(bool $inventory): void
    {
        $this->inventory = $inventory;
    }

    public function decreaseQuantity(int $quantity): void
    {
        $this->quantity -= $quantity;
    }

    public function jsonSerialize()
    {
        return [
            'menuType' => $this->menuType,
            'quantity' => $this->quantity,
            'inventory' => $this->inventory,
        ];
    }
}
<?php

declare(strict_types=1);

namespace App\Doctrine\DBAL\Types;

use App\Entity\Dish\Stock;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\JsonType;

final class StockCollectionType extends JsonType
{
    public function getName()
    {
        return 'stock_collection';
    }

    /**
     * @param $value
     *
     * @throws ConversionException
     *
     * @return array|mixed|null
     */
    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        $value = parent::convertToPHPValue($value, $platform);

        $value = array_values(array_map(fn ($data): Stock => Stock::fromArray($data), $value));

        return new ArrayCollection($value);
    }

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        return parent::convertToDatabaseValue($value->toArray(), $platform);
    }
}

当我对 Dish 实体调用 setStocks 方法时,convertToDatabaseValue 方法被正确调用并且值保存在数据库中,但是当我只编辑一个 属性 的股票,例如调用 descreaseStockQuantity,方法 convertToDatabaseValue 不会被调用,所以数据库中的值不会改变。为什么会这样?

那是因为 Dish(正好是 UnitOfWork)不知道 Stock 的变化。我记得解决这个问题的最简单方法是在 decreaseStockQuantity 方法中更改 Dish 的某些 updated_at 字段。或者您可以使 Stock 不可变并在每次股票变动时重新分配新值。