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
不可变并在每次股票变动时重新分配新值。
我有一个具有自定义列类型 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
不可变并在每次股票变动时重新分配新值。