简体   繁体   中英

What is a Doctrine-centric approach to work with repositories that use entities in mapped superclass configuration?

I have several entities that are related, such as McDoublePrice , CheeseburgerPrice , BigMacPrice , etc, which have a common BurgerPrice entity in a Mapped Superclass Doctrine configuration.

How can I access a particular entity from my controller, action, or request handler? That is, I want the following code to work:

$burgers = array(
    'McDoublePrice' => 4,
    'CheeseburgerPrice' => 5, 
    'BigMacPrice' => 6
);
foreach($burgers as $burger => $burgerId)
{
    $repository = $this->repositoryFactory->getBurgerRepository($burger);
    $entity = $repository->getBurgerEntity($burgerId);
    echo $entity->getBurgerPrice(); // total price of current burger
}

Note: Different $burgerId s can represent different burger styles within the same type of burger. ie Deluxe version, plain version, etc and can use different specific prices for same type of ingredients. ie plain bun vs deluxe bun can have different $burderID and different bun pricing.

Question : How do I structure my code?

Do I create one repository class for each of the BurgerPrice entity? That might create a lot of extra classes and seems wasteful.

Can I get away with a single BurgerRepository class that can handle multiple burgers? If so, how would I set that up in a Doctrine-centric way?

I am looking for a code example or a clear idea on how to do this. I am interested primarily in Doctrine-centric or Doctrine-recommended approach to handling working with multiple polymorphic entities in a Mapped Superclass configuration.

My end goal is to be able to request pricing of specific burgers during runtime.

I have looked into Doctrine Entity Repository pattern , and the repository structure (where I can create custom method names) is something I would like to utilize, but the example does not show how to use it with polymorphic entities.

Sample Entities

/**
 * @ORM\Entity(repositoryClass="BurgerPriceRepository")
 * @Table(name="bigmac", indexes={@Index(name="product_id", columns={"product_id"})})
 * @Entity
 */
class BigMacPrice extends BurgerPrice
{
    /** @var float @Column(name="sauce", type="decimal", precision=6, scale=2, nullable=false) */
    private $sauce;

    function getBurgerPrice(): float
    {
        //secret sauce price
        return 5 * $this->patty + 3 * $this->bun + $this->sauce;
    }
}

/**
 * @ORM\Entity(repositoryClass="BurgerPriceRepository")
 * @Table(name="mcdouble", indexes={@Index(name="product_id", columns={"product_id"})})
 * @Entity
 */
class McDoublePrice extends BurgerPrice
{
    function getBurgerPrice(): float
    {
        //a different price for mcdouble
        return 2 * $this->patty + 2 * $this->bun;
    }
}

abstract class BurgerPrice
{
    /** @var integer @Column(name="id", type="integer", nullable=false) @Id @GeneratedValue(strategy="IDENTITY") */
    protected $id;

    /** @var integer @Column(name="product_id", type="integer", nullable=false) */
    protected $productId;

    /** @var float @Column(name="patty", type="decimal", precision=6, scale=2, nullable=false) */
    public $patty;

    /** @var float @Column(name="bun", type="decimal", precision=6, scale=2, nullable=false) */
    public $bun;

    /**
     * Computes specific product price for a given burger
     *
     * @return float
     */
    abstract function getBurgerPrice(): float;
}

Not certain if it is a Doctrine-recommended approach, or not, but it seems congruent enough to me that I can define a single repository but use different instances of it.

On each polymorphic entity I can follow Doctrine's Entity Repository pattern, and specify

@ORM\Entity(repositoryClass="BurgerPriceRepository")

although it doesn't seem to make a difference if I do not include that line. Maybe it comes handy elsewhere.

And then I can use these classes:

class BurgerPriceRepositoryFactory
{

    /**@var EntityManager*/
    private $entityManager;

    function __construct(EntityManager $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    function getRepository(string $entityName): BurgerPriceRepository
    {
        return new BurgerPriceRepository(
            $this->entityManager, 
            new ClassMetadata($entityName)
        );
    }
}

class BurgerPriceRepository extends EntityRepository
{

    function getBurgerPriceEntity(int $burgerId): BurgerPrice
    {
        return $this->getEntityManager()
            ->getRepository($this->_entityName)
            ->findOneBy(array(
            'productId' => $burgerId
        ));
    }
}

Then use them in my test like so:

/** @var ContainerInterface $container */
/* Service-creation time */
$factory = new ProductPricingRepositoryFactoryFactory();
$this->repositoryFactory = $factory($container);

/* Run-time - Big Mac */
$repository = $this->repositoryFactory->getRepository(BigMacPrice::class);
$entity = $repository->getBurgerPriceEntity($bigMacId);
$this->assertEquals(3.57, $entity->getBurgerPrice());

/* Run-time - McDouble*/
$repository = $this->repositoryFactory->getRepository(McDoublePrice::class);
$entity = $repository->getBurgerPriceEntity($mdDoubleId);
$this->assertEquals(1.39, $entity->getBurgerPrice());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM