Isa*_*way 4 php doctrine symfony
有关 ResultSetMapping 的更新问题,请参阅最后的编辑
\n\n我定义了两个实体(Item 和 ItemType),其中之一与另一个实体具有 ManyToOne 关联。由于生成查找正确项目的一些复杂性,我有相当多的本机查询。这些查询始终返回第一个实体的所有列(SELECT items.* ...)。
\n\n我发现我的关联在第一项上始终为空,并且我不确定我做错了什么。任何帮助,将不胜感激。
\n\n实体:
\n\n\n\n
namespace AppBundle\\Entity;\n\nuse Psr\\Log\\LoggerInterface;\nuse Doctrine\\ORM\\Mapping as ORM;\n\n/**\n * @ORM\\Table(name="items")\n * @ORM\\Entity(repositoryClass="AppBundle\\Entity\\ItemRepository")\n */\nclass Item {\n\n /**\n * @ORM\\Column(type="integer")\n * @ORM\\Id\n * @ORM\\GeneratedValue(strategy="AUTO")\n */\n private $id;\n\n /**\n * @ORM\\Column(name="account_id", type="integer")\n */\n private $accountId;\n\n /**\n * @ORM\\ManyToOne(targetEntity="ItemType")\n * @ORM\\JoinColumn(name="item_type_id", referencedColumnName="id")\n */\n private $itemType;\n\n // ..snip.. //\n\n}\nRun Code Online (Sandbox Code Playgroud)\n\n物品种类
\n\n\n\n
namespace AppBundle\\Entity;\n\nuse Doctrine\\ORM\\Mapping as ORM;\n\n/**\n * @ORM\\Table(name="item_types")\n * @ORM\\Entity(repositoryClass="AppBundle\\Entity\\ItemTypeRepository")\n */\nclass ItemType {\n\n /**\n * @ORM\\Column(type="integer")\n * @ORM\\Id\n * @ORM\\GeneratedValue(strategy="AUTO")\n */\n private $id;\n\n /**\n * @ORM\\Column(name="account_id", type="integer")\n */\n private $accountId;\n\n /**\n * @ORM\\Column(type="string", length=128)\n */\n private $name;\n\n // ..snip.. //\n\n}\nRun Code Online (Sandbox Code Playgroud)\n\n该查询是由 ItemRepository 类的 getItem 方法生成的。这有点长,但可以归结为SELECT items.* FROM items ...通过以下方式运行的查询getEntityManager()->createNativeQuery($sql, $rsm);
\n\n
namespace AppBundle\\Entity;\n\nuse Psr\\Log\\LoggerInterface;\nuse Doctrine\\ORM\\Query\\ResultSetMapping;\n\n/**\n * ItemRepository\n *\n * This class was generated by the Doctrine ORM. Add your own custom\n * repository methods below.\n */\nclass ItemRepository extends \\Doctrine\\ORM\\EntityRepository\n{\n /**\n * @var \\Psr\\Log\\LoggerInterface $logger\n */\n protected $logger;\n\n /**\n * @var ItemTypeRepository\n */\n protected $itemTypes;\n\n /**\n * @var ItemValueRepository\n */\n protected $itemValues;\n\n /**\n * @var FieldRepository\n */\n protected $fields;\n\n /**\n * Called by service bindings in services.yml instead of __construct, which is needed by\n * Doctrine.\n */\n public function initService(LoggerInterface $logger,\n ItemTypeRepository $itemTypes,\n ItemValueRepository $itemValues,\n FieldRepository $fields)\n {\n $this->logger = $logger;\n $this->itemTypes = $itemTypes;\n $this->itemValues = $itemValues;\n $this->fields = $fields;\n }\n\n /**\n * Get items for an account via itemId\n *\n * @param integer $accountId a user\'s account id\n * @param $itemId unique ID for an Item\n * @return Item_model\n */\n public function getItem($accountId, $itemId, $restrictedUserOwnerItemType, $restrictedUserOwnerItemId)\n {\n $this->logger->debug(__METHOD__.\'::params::\'.json_encode([\'accountId\' => $accountId, \'itemId\' => $itemId,\n \'restrictedUserOwnerItemType\' => $restrictedUserOwnerItemType, \'restrictedUserOwnerItemId\' => $restrictedUserOwnerItemId]));\n if(!$accountId || !$itemId || !is_numeric($restrictedUserOwnerItemType) || !is_numeric($restrictedUserOwnerItemId)) \n throw new \\InvalidArgumentException(\'getItem requires accountId, itemId, restrictedUserOwnerItemType and restrictedUserOwnerItemId\');\n\n /*\n $query = $this->itemsModel->builder();\n $result = $query->where(\'account_id\', \'=\', $accountId)\n ->where(\'id\', \'=\', $itemId)\n ->first();\n */\n\n $sql = "SELECT items.*, ".\n "item_types.id AS item_type_id, ".\n "item_types.account_id AS item_type_account_id, ".\n "item_types.name AS item_type_name, ".\n "item_types.plural_name AS item_type_name, ".\n "item_types.label AS item_type_label, ".\n "item_types.plural_label AS item_type_plural_label, ".\n "item_types.are_users AS item_type_are_users, ".\n "item_types.own_users AS item_type_own_users ".\n "FROM items ".\n "JOIN item_types ON item_types.id = items.item_type_id ";\n\n $isRestrictedUser = $restrictedUserOwnerItemType != 0 || $restrictedUserOwnerItemId != 0;\n if($isRestrictedUser)\n {\n // Limit to items that are visible to restricted users\n $sql .= <<<SQL\n\n\n WHERE item_types.visible_to_restricted_users = 1 \n\nSQL;\n\n // Limit to items that contain a relationship field pointed at the same owner item type,\n // with the same item ID. For instance, limit items to those that have a Clients relationship\n // field with "Acme Co." client selected as the client.\n $sql .= <<<SQL\n\nAND items.id IN ( /* Where Item Belongs to Same Owner */\n SELECT item_id \n FROM item_values \n JOIN fields ON fields.id = item_values.field_id\n JOIN items ON items.id = item_values.item_id AND item_values.ver = items.ver\n JOIN item_types ON item_types.id = items.item_type_id\n WHERE item_values.value = ?\n AND fields.field_type = "Relationship"\n AND fields.field_item_type_id = ?)\n\nSQL;\n $params[] = $restrictedUserOwnerItemId; // Example: 3 -- CLIENT ID\n $params[] = $restrictedUserOwnerItemType; // Example: 10 -- CLIENTS\n\n $sql .= "AND ";\n } else {\n $sql .= "WHERE ";\n }\n\n $sql .= "items.account_id = ? AND items.id = ? ";\n $params[] = $accountId;\n $params[] = $itemId;\n\n // Get raw records\n $rsm = $this->standardResultSetMapping();\n // $this->logger->debug($sql);\n // $this->logger->debug(print_r($params, true));\n echo $sql;\n $query = $this->getEntityManager()->createNativeQuery($sql, $rsm);\n $query->setParameters($params);\n\n // Wake up the entities\n $result = array();\n foreach($query->getResult() as $row) {\n $row->initServiceEntity($this->logger, $this, $this->itemValues, $this->fields);\n $result[] = $row;\n }\n\n if(!$result || count($result) == 0)\n throw new \\InvalidArgumentException("Item could not be located for Item #".$itemId.". You may not have permission to view this item or it may not exist.");\n else\n {\n return $result[0];\n }\n }\n\n private function standardResultSetMapping()\n {\n $rsm = new ResultSetMapping();\n // Class, Table\n $rsm->addEntityResult(\'\\AppBundle\\Entity\\Item\', \'items\');\n $rsm->addEntityResult(\'\\AppBundle\\Entity\\ItemType\', \'item_types\');\n // Table, Column, Property\n $rsm->addFieldResult(\'items\', \'id\', \'id\');\n $rsm->addFieldResult(\'items\', \'account_id\', \'accountId\');\n //$rsm->addFieldResult(\'items\', \'item_type_id\', \'itemTypeId\');\n $rsm->addFieldResult(\'items\', \'field_count\', \'fieldCount\');\n $rsm->addFieldResult(\'items\', \'ver\', \'ver\');\n $rsm->addFieldResult(\'items\', \'title\', \'title\');\n $rsm->addMetaResult(\'items\', \'item_type_id\', \'item_type_id\', true);\n\n $rsm->addFieldResult(\'item_types\', \'item_type_id\', \'id\');\n $rsm->addFieldResult(\'item_types\', \'item_type_name\', \'name\');\n $rsm->addFieldResult(\'item_types\', \'item_type_plural_name\', \'pluralName\');\n $rsm->addFieldResult(\'item_types\', \'item_type_label\', \'label\');\n $rsm->addFieldResult(\'item_types\', \'item_type_plural_label\',\'pluralLabel\');\n $rsm->addFieldResult(\'item_types\', \'item_type_are_users\', \'areUsers\');\n $rsm->addFieldResult(\'item_types\', \'item_type_own_users\', \'ownUsers\');\n\n return $rsm;\n }\n\n}\nRun Code Online (Sandbox Code Playgroud)\n\n返回 Item 实体,但 itemType 始终为空:
\n\nItem {#548 \xe2\x96\xbc\n -id: 23\n -accountId: 1\n -itemType: null\n -fieldCount: 4\n -ver: 1451940837\n -title: "New Item"\n #fields: []\n #itemValues: []\n #cacheValues: []\n #logger: Logger {#268 \xe2\x96\xb6}\n #itemsRepository: ItemRepository {#349 \xe2\x96\xb6}\n #itemValuesRepository: ItemValueRepository {#416 \xe2\x96\xb6}\n #fieldsRepository: FieldRepository {#338 \xe2\x96\xb6}\n #loaded: true\n #changeCount: 0\n}\nRun Code Online (Sandbox Code Playgroud)\n\nitem_types 数据
\n\nid account_id name plural_name label plural_label are_users own_users\n31 1 task tasks Task Tasks 1 0\nRun Code Online (Sandbox Code Playgroud)\n\n项目数据
\n\nid account_id item_type_id field_count ver title\n23 1 31 4 1451940837 New Item\nRun Code Online (Sandbox Code Playgroud)\n\n编辑我认为,我已将其范围缩小到 ResultSetMapping 配置。更新了上面的代码。结果现在返回两个不同的对象,但没有将它们连接起来(Item\'s itemType 仍然为 null):
\n\nobject(AppBundle\\Entity\\Item)[560]\n private \'id\' => int 23\n private \'accountId\' => int 1\n private \'itemType\' => null\n private \'fieldCount\' => int 4\n private \'ver\' => int 1451940837\n private \'title\' => string \'New Item\' (length=8)\n protected \'fields\' => \n array (size=0)\n empty\n protected \'itemValues\' => \n array (size=0)\n empty\n protected \'cacheValues\' => \n array (size=0)\n empty\n protected \'logger\' => null\n protected \'itemsRepository\' => null\n protected \'itemValuesRepository\' => null\n protected \'fieldsRepository\' => null\n protected \'loaded\' => boolean false\n protected \'changeCount\' => int 0\nobject(AppBundle\\Entity\\ItemType)[507]\n private \'id\' => int 31\n private \'accountId\' => int 1\n private \'name\' => string \'task\' (length=4)\n private \'pluralName\' => string \'tasks\' (length=5)\n private \'label\' => string \'Task\' (length=4)\n private \'pluralLabel\' => string \'Tasks\' (length=5)\n private \'areUsers\' => boolean true\n private \'ownUsers\' => boolean false\nRun Code Online (Sandbox Code Playgroud)\n\n所以现在的问题基本上是:
\n\n如何设置 ResultSetMapping 以便它返回所有连接关联完好无损的实体?
\nDoctrine关于 Native SQL 的文档有一些很好的见解,并且它非常清楚地表明您的错误是什么。对您现有帖子的简短回答是,您应该使用addJoinedEntityResult()而不是addEntityResult()用于您的ItemType实体。
实体结果的文档指出:
实体结果描述在转换结果中显示为根元素的实体类型。
这意味着,如果您在同一映射中添加两个实体结果,您将获得当前看到的结果 - Item和ItemType作为两个不同的对象返回。但是,您知道两者是相关的,因此连接实体结果更有意义:
连接实体结果描述了一种实体类型,该实体类型在转换结果中显示为连接关系元素,附加到(根)实体结果。
要直接按原样修复代码,您需要更改
$rsm->addEntityResult('\AppBundle\Entity\ItemType', 'item_types');
Run Code Online (Sandbox Code Playgroud)
对此:
$rsm->addJoinedEntityResult(
'\AppBundle\Entity\ItemType',
'item_types',
'items',
'itemType'
);
Run Code Online (Sandbox Code Playgroud)
格式为addJoinedEntityResult($class, $alias, $parentAlias, $relation),因此您可以看到添加的第三个和第四个参数指向父别名,以及Item类中指向ItemType 的字段。
话虽这么说,我认为你让这变得太复杂了,并且可以通过使用 ResultSetMappingBuilder 大大简化你的代码。这可以自动将字段映射到其等效的 SQL 列,然后,如果您更改了数据库中的字段名称或列名称,则无需挖掘所有代码来更新映射。
因此,standardResultSetMapping()您可以简单地执行以下操作,而不是调用复杂的函数:
$rsm = new ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata('AppBundle\Entity\Item', 'items');
$rsm->addJoinedEntityFromClassMetadata('AppBundle\Entity\ItemType', 'item_types', 'items', 'itemType',
['id' => 'item_type_id',
'account_id' => 'item_type_id',
'name' => 'item_type_name',
'plural_name' => 'item_type_plural_name',
'label' => 'item_type_label',
'plural_label' => 'item_type_plural_label',
'are_users' => 'item_type_are_users',
'own_users' => 'item_type_own_users']
);
Run Code Online (Sandbox Code Playgroud)
这样,您就可以消除冗余代码,使其不易出错,更易于测试,并自动处理实体和数据库的更新。第二次调用表明您仍然可以传递重命名列的数组。
| 归档时间: |
|
| 查看次数: |
2676 次 |
| 最近记录: |