gpt4 book ai didi

php - 在具有串联 ID 的数据库字段上创建学说关联

转载 作者:行者123 更新时间:2023-11-29 02:21:28 26 4
gpt4 key购买 nike

我正在现有的 MySQL 数据库之上构建一个 Symfony 2/Doctrine 2 应用程序。由于过去的错误决定,我坚持使用在表列中串联的引用。不幸的是,重构数据库不是一种选择。

例如引用多个“类别”的实体“产品”:

| id | name      | category_ids |
|----|-----------|--------------|
| 1 | product a | 1,2,5 |
| 2 | product b | 3,4,1 |
| 3 | product c | 2 |

我希望在我的“产品”实体中使用 getCategories 方法,它会返回类别对象的集合。

有什么方法可以用 Doctrine 实现这个目标吗?

也许使用基于“FIND_IN_SET”的自定义代码?

SELECT c.* 
FROM product p
LEFT OUTER JOIN category c ON FIND_IN_SET(c.id, p.category_ids)
WHERE p.id=:product_id;

或者定义与分解值的关联?

explode(',',$this->category_ids)

我尽量避免每次需要从我的产品实体中检索类别时都必须使用 EntityManager。因为:

  • 在实体中注入(inject) EntityManager 是不好的做法
  • 每次在我的 Controller 中使用 EntityManager 有点违反 DRY 原则
  • 我不知道如何在 Symfony FormTypes 中实现这一点,使 Choice/Entity 字段包含产品的相关类别。

最佳答案

方案一

您可以为您的 getCategories 方法制定一个水化策略,并在您的水化器类中注册该策略(甚至可以是 DoctrineObject 水化器)。像这样的东西:

策略

<?php

namespace My\Hydrator\Strategy;

use Doctrine\Common\Collections\ArrayCollection;
use My\Entity\Category;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use Zend\Stdlib\Hydrator\Strategy\StrategyInterface;

class CategoriesStrategy implements StrategyInterface, ObjectManagerAwareInterface
{
/**
* @var ObjectManager
*/
protected $objectManager;

/**
* @param ObjectManager $objectManager
* @param String $hostName
*/
public function __construct(ObjectManager $objectManager)
{
$this->objectManager = $objectManager;
}

/**
* @param array $value
* @return ArrayCollection
*/
public function extract($value)
{
$collection = new ArrayCollection();

if (is_array($value)) {
foreach ($value as $id) {
$category = $this->getObjectManager()->find(Category::class, $id);
$collection->add($category);
}
}

return $collection;
}

/**
* @param ArrayCollection $value
* @return array
*/
public function hydrate($value)
{
$array = array();
/** @var Category $category */
foreach ($value as $category) {
$array[] = $category->getId();
}
return $array;
}

/**
* @param ObjectManager $objectManager
* @return $this
*/
public function setObjectManager(ObjectManager $objectManager)
{
$this->objectManager = $objectManager;

return $this;
}

/**
* @return ObjectManager
*/
public function getObjectManager()
{
return $this->objectManager;
}
}

您可能需要一个工厂来在您的水化器类中注册您的 CategoriesStrategy:

水龙头工厂

<?php

namespace My\Hydrator;

use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject;
use My\Hydrator\Strategy\CategoriesStrategy;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\ServiceManager;

class MyHydratorFactory implements FactoryInterface
{
/**
* @param ServiceLocatorInterface $serviceLocator
* @return DoctrineObject
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
/** @var ServiceManager $serviceManager */
$serviceManager = $serviceLocator->getServiceLocator();

/** @var ObjectManager $objectManager */
$objectManager = $serviceManager->get('bc_object_manager');

/** @var DoctrineObject $hydrator */
$hydrator = new DoctrineObject($objectManager);
$hydrator->addStrategy('categories', new CategoriesStrategy($objectManager));
return $hydrator;
}
}

这没有经过测试,但你明白了......


解决方案2

另一种解决方案是为您的类别注册一个 DBAL 类型。您可以在 the Doctrine2 documentation chapter 8.4. Custom Mapping Types 中查看如何执行此操作.

在您的实体列定义中,您指向一个类别类型:

/**
* @var string
* @ORM\Column(type="categories")
*/
protected $categories;

你在教义中注册的魔法是这样的:

'doctrine' => array(
'configuration' => array(
'orm_default' => array(
'types' => array(
'categories' => 'My\DBAL\Types\CategoriesCollection '
)
)
)
)

然后是类本身:

<?php

namespace My\DBAL\Types;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\Common\Collections\Collection;

class CategoriesCollection extends \Doctrine\DBAL\Types\Type
{
const NAME = 'categories';

/**
* @return string
*/
public function getName()
{
return self::NAME;
}

/**
* {@inheritdoc}
*/
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return $platform->getDoctrineTypeMapping('simple_array');
}

/**
* @param Collection $collection
* @param AbstractPlatform $platform
* @return array
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
$array = [];
foreach($value as $category)
{
$category_id = $category->getId();
array_push($array, $category_id);
}
return $array;
}

/**
* {@inheritdoc}
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
$collection = new ArrayCollection();
if ($value === null) {
return $collection;
}

foreach($value as $category_id){
$category = $this->em->getReference('Vendor\Bundle\Entity\Category', $category_id);
$collection->add($category);
}

return $collection;
}

/**
* @var EntityManager
*/
protected $em;


/**
* @param EntityManager $entityManager
*/
public function setEntityManager(EntityManager $entityManager)
{
$this->em = $entityManager;
}
}

此解决方案实际上与其他解决方案相同,只是您使用 Doctrine2 内部实现。您仍然需要在您的 DBAL 类型中注册 EntityManager 并且不确定最简单的方法是什么,所以我留给您。

在 Symfony 中,您可以在 app/config/config.yml 文件中注册自定义映射类型

doctrine:
dbal:
types:
category_ids: Vendor\Bundle\Type\CategoriesCollection

您可以在包的启动序列中注入(inject) EntityManager 依赖项:

<?php

namespace Vendor\Bundle\Bundle;

use Doctrine\DBAL\Types\Type;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Trilations\TApp\CoreBundle\Type\CategoryCollectionType;

class VendorBundleBundle extends Bundle
{
public function boot()
{
$em = $this->container->get('doctrine.orm.default_entity_manager');
$categoryCollectionType = Type::getType('category_ids');
$categoryCollectionType->setEntityManager($em);
}
}

并将字段映射到正确的自定义映射:

Vendor\Bundle\Enitity\Product
table: product
fields:
categories: category_ids

关于php - 在具有串联 ID 的数据库字段上创建学说关联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31140344/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com