OneToMany还是OneToOne,我在正确或错误的路径? [英] OneToMany or OneToOne, I'm in the right or wrong path?
问题描述
我有这个数据库模型:
然后我做了这个实体(我只是离开关系部分,因为另一个与主题无关):
Orders.php
类订单{
/ **
* @ ORM\ManyToOne(targetEntity =Person,inversedBy =orders)
* @ ORM\JoinColumn(name =person_id,referencedColumnName =id)
* * /
保护$ person;
public function setPerson(Person $ person)
{
$ this-> person = $ person;
return $ this;
}
public function getPerson()
{
return $ this-> person;
}
}
.php
class Person {
/ **
* @ORM \OneToMany(targetEntity =NaturalPerson,mappedBy =person)
* * /
private $ naturals;
/ **
* @ ORM\OneToMany(targetEntity =LegalPerson,mappedBy =person)
* * /
private $ legals;
/ **
* @ ORM\OneToMany(targetEntity =Orders,mappedBy =person)
* * /
private $ orders;
public function __construct()
{
$ this-> naturals = new ArrayCollection();
$ this-> legals = new ArrayCollection();
$ this-> orders = new ArrayCollection();
}
public function getNaturals()
{
return $ this-> naturals;
}
public function getLegals()
{
return $ this->
}
public function getOrders()
{
return $ this-> orders;
}
}
NaturalPerson .php
class NaturalPerson {
/ **
* @ ORM\Id
* @ ORM\ManyToOne(targetEntity =Person,inversedBy =naturals)
* @ ORM\JoinColumn(name =person_id,referencedColumnName = id)
* /
protected $ person;
/ **
* @ ORM\Column(name =identify_type,type =ci_type,nullable = false)
* @ DoctrineAssert\Enum =Tanane\FrontendBundle\DBAL\Types\CIType)
* /
protected $ identification_type;
/ **
* @ ORM\Column(name =ci,type =integer,nullable = false)
* /
protected $ CI;
public function setPerson(Person $ person)
{
$ this-> person = $ person;
return $ this;
}
public function getPerson()
{
return $ this-> person;
}
public function setIdentificationType($ identification_type)
{
$ this-> identify_type = $ identification_type;
return $ this;
}
public function getIdentificationType()
{
return $ this-> identification_type;
}
public function setCI($ ci)
{
$ this-> ci = $ ci;
return $ this;
}
public function getCI()
{
return $ this-> ci;
}
}
我省略了 LegalPerson
,因为它与 NaturalPerson
几乎一样,所以这里是问题。映射看起来不错,但是如何从 Orders
获取相关记录?
这个想法是针对每个订单
我需要知道哪个 Person
属于(订单),还有存储在 NaturalPerson
或 LegalPerson
中的额外信息取决于 person.type
。
请看这段代码:
public function getOrdersAction()
{
$ response = array();
$ em = $ this-> getDoctrine() - > getManager();
$ entities = $ em-> getRepository(FrontendBundle:Orders) - > findAll();
如果(!$ entities)
{
$ response ['message'] =否se encontraron resultados;
}
$ orders = array();
foreach($ entities as $ entity)
{
$ personType = $ entity-> getPerson() - > getPersonType();
$ order = array();
$ order [] = $ entity-> getNickname();
//这里我正在尝试从`Orders`获取`Naturals`方法
if($ personType == 1)
{
$ order [ ] = $ entity-> getPerson() - > getNaturals()[0] - > getIdentificationType()。 $实体 - > getPerson() - > getNaturals()[0] - > getCI();
}
elseif($ personType == 2)
{
$ order [] = $ entity-> getPerson() - > getLegals()[0] ; getIdentificationType()。 $实体 - > getPerson() - > getLegals()[0] - > getRIF();
}
$ orders [] = $ order;
}
$ response ['data'] = $ orders;
返回新的JsonResponse($ response);
}
但是我收到这个错误:
错误:调用
/ var / www / html / tanane / src / Tanane / BackendBundle中
非对象的成员函数getIdentificationType() /Controller/OrderController.php
第115行
也许我的映射是错误的,因为我应该有
(DER显示的我的逻辑听起来错了)或者也许不是,但是我不知道如何获取相关的属性只有一个记录,我阅读文档 ) Person
和 NaturalPerson
之间的OneToOne
你会有:
Person.php
/ **
* @ ORM\Table(name =person)
* @ ORM\Entity
* @ ORM\InheritanceType(JOINED)
* @ ORM\\DiscriminatorColumn(name =discr,type =string)
* @ ORM\DiscriminatorMap({
*natural=NaturalPerson,
*legal=LegalPerson,
*})
* /
class人{
/ **
* @ ORM\OneToMany(targetEntity =Orders,mappedBy =person)
* * /
private $ orders;
public function __construct()
{
$ this-> orders = new ArrayCollection();
}
public function getOrders()
{
return $ this-> orders;
}
}
NaturalPerson .php
/ **
* @ ORM\Table(name = natural_person)
* @ ORM\Entity
* /
class NaturalPerson extends Person {
/ **
* @ ORM\Column(name =identify_type ,type =ci_type,nullable = false)
* @ DoctrineAssert\Enum(entity =Tanane\FrontendBundle\DBAL\Types\CIType)
* /
protected $ identification_type;
/ **
* @ ORM\Column(name =ci,type =integer,nullable = false)
* /
protected $ CI;
public function setIdentificationType($ identification_type)
{
$ this-> identify_type = $ identification_type;
return $ this;
}
public function getIdentificationType()
{
return $ this-> identify_type;
}
public function setCI($ ci)
{
$ this-> ci = $ ci;
return $ this;
}
public function getCI()
{
return $ this-> ci;
}
}
Order.php
保持不变。
如您所见,现在既有 NaturalPerson
和 LegalPerson
extension Person
。由于您更改了实体定义,因此您必须更新数据库模式。
现在,您的控制器
你只需要这样做:
foreach($ entities as $ entity)
{
$ person = $ entity-> getPerson();
$ order = array();
$ order [] = $ entity-> getNickname();
if($ person instanceof NaturalPerson)
{
$ order [] = $ person-> getIdentificationType()。 $人 - > getCI();
}
else //它必须是LegalPerson
{
$ order [] = $ person-> getIdentificationType()。 $人 - > getRIF();
}
$ orders [] = $ order;
}
不要忘记添加 use
声明
NaturalPerson
!
这样你只能处理 NaturalPerson
或 LegalPerson
。我相信你可以进一步改善这一点。
最后,你必须改变你的CRUD。您不再直接使用 Person
(实际上应该是 abstract
),所以现在你需要分别处理 NaturalPerson
和 LegalPerson
的CRUD。每个都将有类型
,控制器
,视图等。
您的代码现在如下所示:
if($ person_type === 1)
{
$ entityPerson = new NaturalPerson();
$ entityPerson-> setDescription($ orders ['nat'] ['person'] ['description']);
$ entityPerson-> setContactPerson($ orders ['nat'] ['person'] ['contact_person']);
$ entityPerson-> setIdentificationType($ orders ['nat'] ['identification_type']);
$ entityPerson-> setCI($ orders ['nat'] ['ci']);
$ em-> persist($ entityPerson);
$ em-> flush();
}
elseif($ person_type === 2)
{
$ entityPerson = new LegalPerson();
$ entityPerson-> setDescription($ orders ['leg'] ['person'] ['description']);
$ entityPerson-> setContactPerson($ orders ['leg'] ['person'] ['contact_person']);
$ entityPerson-> setIdentificationType($ orders ['leg'] ['identification_type']);
$ entityPerson-> setRIF($ orders ['leg'] ['rif']);
$ em-> persist($ entityPerson);
$ em-> flush();
}
I have this DB model:
Then I made this entities (I just leave the relation part since the other isn't relevant on the topic):
Orders.php
class Orders {
/**
* @ORM\ManyToOne(targetEntity="Person", inversedBy="orders")
* @ORM\JoinColumn(name="person_id", referencedColumnName="id")
* */
protected $person;
public function setPerson(Person $person)
{
$this->person = $person;
return $this;
}
public function getPerson()
{
return $this->person;
}
}
Person.php
class Person {
/**
* @ORM\OneToMany(targetEntity="NaturalPerson", mappedBy="person")
* */
private $naturals;
/**
* @ORM\OneToMany(targetEntity="LegalPerson", mappedBy="person")
* */
private $legals;
/**
* @ORM\OneToMany(targetEntity="Orders", mappedBy="person")
* */
private $orders;
public function __construct()
{
$this->naturals = new ArrayCollection();
$this->legals = new ArrayCollection();
$this->orders = new ArrayCollection();
}
public function getNaturals()
{
return $this->naturals;
}
public function getLegals()
{
return $this->legals;
}
public function getOrders()
{
return $this->orders;
}
}
NaturalPerson.php
class NaturalPerson {
/**
* @ORM\Id
* @ORM\ManyToOne(targetEntity="Person", inversedBy="naturals")
* @ORM\JoinColumn(name="person_id", referencedColumnName="id")
*/
protected $person;
/**
* @ORM\Column(name="identification_type", type="ci_type", nullable=false)
* @DoctrineAssert\Enum(entity="Tanane\FrontendBundle\DBAL\Types\CIType")
*/
protected $identification_type;
/**
* @ORM\Column(name="ci", type="integer", nullable=false)
*/
protected $ci;
public function setPerson(Person $person)
{
$this->person = $person;
return $this;
}
public function getPerson()
{
return $this->person;
}
public function setIdentificationType($identification_type)
{
$this->identification_type = $identification_type;
return $this;
}
public function getIdentificationType()
{
return $this->identification_type;
}
public function setCI($ci)
{
$this->ci = $ci;
return $this;
}
public function getCI()
{
return $this->ci;
}
}
I omitted LegalPerson
since it's pretty much the same as NaturalPerson
so here is the problem. The mapping looks good but how I do get related records from Orders
?
The idea behind this is for each Orders
I need to know to which Person
belongs too (the Orders) and also the extra information stored at NaturalPerson
or LegalPerson
depending on person.type
.
See this code:
public function getOrdersAction()
{
$response = array();
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository("FrontendBundle:Orders")->findAll();
if (!$entities)
{
$response['message'] = "No se encontraron resultados";
}
$orders = array();
foreach ($entities as $entity)
{
$personType = $entity->getPerson()->getPersonType();
$order = array();
$order[] = $entity->getNickname();
// Here I'm trying to access to `Naturals` methods from `Orders`
if ($personType == 1)
{
$order[] = $entity->getPerson()->getNaturals()[0]->getIdentificationType() . $entity->getPerson()->getNaturals()[0]->getCI();
}
elseif ($personType == 2)
{
$order[] = $entity->getPerson()->getLegals()[0]->getIdentificationType() . $entity->getPerson()->getLegals()[0]->getRIF();
}
$orders[] = $order;
}
$response['data'] = $orders;
return new JsonResponse($response);
}
But I get this error:
Error: Call to a member function getIdentificationType() on a non-object in /var/www/html/tanane/src/Tanane/BackendBundle/Controller/OrderController.php line 115
Maybe my mapping is wrong since I should have OneToOne
between Person
and NaturalPerson
(and that sounds wrong to my logic as DER shows) or maybe is not, but then I don't know how to fetch related properties for just one record, I read docs here and also in here but they didn't talk about this part or I don't see it, any advice? ideas? tips?
Trying to use Repositories and DQL to solve the problem
I'm building a function in a Repository
class to fetch the data and not get to complicated as apparently my problem is, so I did this:
public function getOrders($person_type = 1)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('ord.*, ps.*')
->from("FrontendBundle:Orders", "ord")
->join('FrontendBUndle:Person', 'ps', 'WITH', 'ps.id = ord.person_id')
->orderBy('ord.created', 'DESC');
if ($person_type == 1)
{
$qb
->select('np.*')
->join('FrontendBundle:NaturalPerson', 'np', 'WITH', 'ps.id = np.person'); // Join NaturalPerson table
}
elseif ($person_type == 2)
{
$qb
->select('lp.*')
->join('FrontendBundle:LegalPerson', 'lp', 'WITH', 'ps.id = lp.person'); // Join NaturalPerson table
}
return $qb->getQuery()->getResult();
}
I'm not tested yet so maybe it won't works but, if the idea is to get the extra information for both tables, then using this DQL I made how I pass the $person_type
which is inside Person
table? This is getting a little complicated, at least for me
Running a raw query to see if columns are NULL
I build this simple query just for test if results are NULL
:
SELECT
ord.id,
ord.person_id as ord_person_id,
ord.nickname,
ps.id,
ps.description,
np.person_id as natural_person_id,
np.identification_type,
np.ci
FROM
orders ord
LEFT JOIN person ps ON ord.person_id = ps.id
LEFT JOIN natural_person np ON np.person_id = ps.id
WHERE
ps.person_type = 1;
And this what query returns:
So there is not NULL columns in there
CRUD for create new Orders
// Set Person entity
$entityPerson = new Person();
$person_type === 1 ? $entityPerson->setDescription($orders['nat']['person']['description']) : $entityPerson->setDescription($orders['leg']['person']['description']);
$person_type === 1 ? $entityPerson->setContactPerson($orders['nat']['person']['contact_person']) : $entityPerson->setContactPerson($orders['leg']['person']['contact_person']);
$entityPerson->setPersonType($person_type);
$em->persist($entityPerson);
$em->flush();
...
if ($person_type === 1)
{
// Set NaturalPerson entity
$entityNatural = new NaturalPerson();
$entityNatural->setIdentificationType($orders['nat']['identification_type']);
$entityNatural->setCI($orders['nat']['ci']);
$em->persist($entityNatural);
$em->flush();
}
elseif ($person_type === 2)
{
// Set LegalPerson entity
$entityLegal = new LegalPerson();
$entityLegal->setIdentificationType($orders['leg']['identification_type']);
$entityLegal->setRIF($orders['leg']['rif']);
$em->persist($entityLegal);
$em->flush();
}
Since LegalPerson
and NaturalPerson
are specializations of Person
I would recommend using what Doctrine calls Class Table Inheritance (documentation).
You would have:
Person.php
/**
* @ORM\Table(name="person")
* @ORM\Entity
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="discr", type="string")
* @ORM\DiscriminatorMap({
* "natural" = "NaturalPerson",
* "legal" = "LegalPerson",
* })
*/
class Person {
/**
* @ORM\OneToMany(targetEntity="Orders", mappedBy="person")
* */
private $orders;
public function __construct()
{
$this->orders = new ArrayCollection();
}
public function getOrders()
{
return $this->orders;
}
}
NaturalPerson.php
/**
* @ORM\Table(name="natural_person")
* @ORM\Entity
*/
class NaturalPerson extends Person {
/**
* @ORM\Column(name="identification_type", type="ci_type", nullable=false)
* @DoctrineAssert\Enum(entity="Tanane\FrontendBundle\DBAL\Types\CIType")
*/
protected $identification_type;
/**
* @ORM\Column(name="ci", type="integer", nullable=false)
*/
protected $ci;
public function setIdentificationType($identification_type)
{
$this->identification_type = $identification_type;
return $this;
}
public function getIdentificationType()
{
return $this->identification_type;
}
public function setCI($ci)
{
$this->ci = $ci;
return $this;
}
public function getCI()
{
return $this->ci;
}
}
Order.php
stays the same.
As you can see, now both NaturalPerson
and LegalPerson
extend Person
. Since you've changed your entities definition, you'll have to update your database schema.
Now, in your Controller
you only have to do this:
foreach ($entities as $entity)
{
$person = $entity->getPerson();
$order = array();
$order[] = $entity->getNickname();
if ($person instanceof NaturalPerson)
{
$order[] = $person->getIdentificationType() . $person->getCI();
}
else // it has to be LegalPerson
{
$order[] = $person->getIdentificationType() . $person->getRIF();
}
$orders[] = $order;
}
Don't forget to add the use
statement for NaturalPerson
!
This way you only work with instances of either NaturalPerson
or LegalPerson
. I'm sure you can further improve this.
Lastly, you will have to change your CRUD for this. You don't work directly with Person
anymore (in fact, it should be abstract
), so now you need to handle CRUD for NaturalPerson
and for LegalPerson
separately. Each will have its Type
, Controller
, views, etc.
Your code would now look like this:
if ($person_type === 1)
{
$entityPerson = new NaturalPerson();
$entityPerson->setDescription($orders['nat']['person']['description']);
$entityPerson->setContactPerson($orders['nat']['person']['contact_person']);
$entityPerson->setIdentificationType($orders['nat']['identification_type']);
$entityPerson->setCI($orders['nat']['ci']);
$em->persist($entityPerson);
$em->flush();
}
elseif ($person_type === 2)
{
$entityPerson = new LegalPerson();
$entityPerson->setDescription($orders['leg']['person']['description']);
$entityPerson->setContactPerson($orders['leg']['person']['contact_person']);
$entityPerson->setIdentificationType($orders['leg']['identification_type']);
$entityPerson->setRIF($orders['leg']['rif']);
$em->persist($entityPerson);
$em->flush();
}
这篇关于OneToMany还是OneToOne,我在正确或错误的路径?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!