OneToMany还是OneToOne,我在正确或错误的路径? [英] OneToMany or OneToOne, I'm in the right or wrong path?

查看:140
本文介绍了OneToMany还是OneToOne,我在正确或错误的路径?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个数据库模型:



然后我做了这个实体(我只是离开关系部分,因为另一个与主题无关):



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行


也许我的映射是错误的,因为我应该有 Person NaturalPerson 之间的OneToOne (DER显示的我的逻辑听起来错了)或者也许不是,但是我不知道如何获取相关的属性只有一个记录,我阅读文档 )



你会有:



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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆