简体   繁体   中英

Generating schema for many to one relation

I have a Message class and want to extend a Ticket class that is going to be some kind of support tickets class, so they might have another field called 'status', for instance.

The parent class:

namespace PrivateMessageBundle\Entity; 

use Doctrine\ORM\Mapping as ORM;
use MedApp\CrudBundle\Entity\User;

/**
 * Message
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Message
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(name="title", type="string", length=50)
     */
    protected $title;

    /**
     * @ORM\ManyToOne(targetEntity="MedApp\CrudBundle\Entity\User")
     * @ORM\JoinColumn(referencedColumnName="id")
     */
    protected $receiver;
    /**
     * @ORM\ManyToOne(targetEntity="MedApp\CrudBundle\Entity\User")
     * @ORM\JoinColumn(referencedColumnName="id")
     */
    protected $sender;

    /**
     * @var string
     *
     * @ORM\Column(name="content", type="string", length=2000)
     */
    protected $content;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="date", type="datetime")
     */
    protected $date;


    /**
     * @var boolean
     *
     * @ORM\Column(name="is_spam", type="boolean")
     */
    protected $is_spam=false;


    /**
     * @var \DateTime
     *
     * @ORM\Column(name="seen_at", type="datetime",nullable=true)
     */
    protected $seen_at=null;


//autogenerated functions here
}

As you can see, it has a many to one relationship with my User class on the fields receiver and sender. This class is generated just fine.

The child class that I want to extend from the Message class:

    namespace SupportMessageBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use PrivateMessageBundle\Entity\Message;

/**
 * Ticket
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Ticket extends Message
{

    /**
     * @var integer
     */
    private $id;

    /**
     * @var string
     */
    private $title;

    /**
     * @var string
     */
    private $content;

    /**
     * @var \DateTime
     */
    private $date;

    /**
     * @var boolean
     */
    private $is_spam;

    /**
     * @var \DateTime
     */
    private $seen_at;

    /**
     * @var \MedApp\CrudBundle\Entity\User
     */
    private $receiver;

    /**
     * @var \MedApp\CrudBundle\Entity\User
     */
    private $sender;

//auto generated functions
}

There are a few problems with this class. It was empty, but I generated the fields and functions with doctrine:generate:entities SupportMessageBundle.

First, it generates the field private, and at schema:update I get

Compile Error: Access level to SupportMessageBundle\Entity\Ticket::$id must  
   be protected (as in class PrivateMessageBundle\Entity\Message) or weaker 

So I change all fields to protected and it generates my tables in database, but without the sender and receiver id. Any ideas how can I make it do that, too? Or why are my fields made private in the first place?

Note that I want Ticket to still have Message's fields, I don't want some of my Messages to be Tickets.

Something like that will do it. But you should follow @StuBez comment and read https://doctrine-orm.readthedocs.org/en/latest/reference/inheritance-mapping.html

This is a JOINED exemple witch have performance inpact in certain use case.

<?php

namespace PrivateMessageBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap({"ticket" = "Ticket")
 */
class Message
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(name="title", type="string", length=50)
     */
    protected $title;
    // ...
}

/** @ORM\Entity */
class Ticket extends Message
{
    // ... New fields don't repeat parent one
}

It always depend on what you want to design. First of all lets say, that the generator does not work for your propose so i recommend, not using it in this case.

Something I changed:

  • I use two bundles PrivateMessageBundle and PrivateTicketBundle both under the namespace Acme
  • I changed the properties $is_spam and $seen_at to $isSeen and $seenAt because of the symfony code style

Inheritance mapping

The correct class definition, if you want to use inheritance mapping is:

<?php

namespace Acme\PrivateMessageBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use AppBundle\Entity\User;

/**
 * Message
 *
 * @ORM\Table(name="message")
 * @ORM\Entity()
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap({"message"="Message", "ticket" = "Acme\PrivateTicketBundle\Entity\Ticket"})
 *
 */
class Message
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(name="title", type="string", length=50)
     */
    protected $title;

    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
     * @ORM\JoinColumn(referencedColumnName="id")
     */
    protected $receiver;
    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
     * @ORM\JoinColumn(referencedColumnName="id")
     */
    protected $sender;

    /**
     * @var string
     *
     * @ORM\Column(name="content", type="string", length=2000)
     */
    protected $content;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="date", type="datetime")
     */
    protected $date;


    /**
     * @var boolean
     *
     * @ORM\Column(name="is_spam", type="boolean")
     */
    protected $isSpam = false;


    /**
     * @var \DateTime
     *
     * @ORM\Column(name="seen_at", type="datetime",nullable=true)
     */
    protected $seenAt = null;

// [...] skip constructor, getter, setter and other methods
 }

And the Ticket class looks like this

<?php
namespace Acme\PrivateTicketBundle\Entity;

use Acme\PrivateMessageBundle\Entity\Message;
use Doctrine\ORM\Mapping as ORM;

/**
 * Ticket extending Message
 *
 * @ORM\Table(name="ticket")
 * @ORM\Entity()
 */
class Ticket extends Message
{
    /**
     * @var string
     * @ORM\Column(name="status", type="string")
     */
    protected $status;

    /**
     * Set status
     *
     * @param string $status
     * @return Ticket
     */
    public function setStatus($status)
    {
        $this->status = $status;

        return $this;
    }

    /**
     * Get status
     *
     * @return string 
     */
    public function getStatus()
    {
        return $this->status;
    }
}

this will generate the following tables:

CREATE TABLE message (id INTEGER NOT NULL, receiver_id INTEGER DEFAULT NULL, sender_id INTEGER DEFAULT NULL, title VARCHAR(50) NOT NULL, content VARCHAR(2000) NOT NULL, date DATETIME NOT NULL, is_spam BOOLEAN NOT NULL, seen_at DATETIME DEFAULT NULL, discr VARCHAR(255) NOT NULL, PRIMARY KEY(id));
CREATE TABLE ticket (id INTEGER NOT NULL, status VARCHAR(255) NOT NULL, PRIMARY KEY(id));

One for the message entity with all fields defined for the message and a table for ticket just with and id (which will always be the same as the corresponding id in the message table) and the extra field status.

Two tables with the same columns

This is also possible using traits: Message entity:

<?php

namespace Acme\PrivateMessageBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Message
 *
 * @ORM\Table(name="message")
 * @ORM\Entity()
 */
class Message
{
  // Use the MessageTraid
  use MessageTrait;
}

(You have to keep the use Doctrine\\ORM\\Mapping as ORM; !)

Ticket entity:

<?php
namespace Acme\PrivateTicketBundle\Entity;

use Acme\PrivateMessageBundle\Entity\MessageTrait;
use Doctrine\ORM\Mapping as ORM;

/**
 * Ticket extending Message
 *
 * @ORM\Table(name="ticket")
 * @ORM\Entity()
 */
class Ticket
{
    // Use the MessageTraid
    use MessageTrait;

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

    /**
     * Set status
     *
     * @param string $status
     * @return Ticket
     */
    public function setStatus($status)
    {
        $this->status = $status;

        return $this;
    }

    /**
     * Get status
     *
     * @return string 
     */
    public function getStatus()
    {
        return $this->status;
    }

}

And here is the Trait:

<?php

namespace Acme\PrivateMessageBundle\Entity;


trait MessageTrait
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(name="title", type="string", length=50)
     */
    protected $title;

    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
     * @ORM\JoinColumn(referencedColumnName="id")
     */
    protected $receiver;
    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
     * @ORM\JoinColumn(referencedColumnName="id")
     */
    protected $sender;

    /**
     * @var string
     *
     * @ORM\Column(name="content", type="string", length=2000)
     */
    protected $content;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="date", type="datetime")
     */
    protected $date;


    /**
     * @var boolean
     *
     * @ORM\Column(name="is_spam", type="boolean")
     */
    protected $isSpam = false;


    /**
     * @var \DateTime
     *
     * @ORM\Column(name="seen_at", type="datetime",nullable=true)
     */
    protected $seenAt = null;


    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set title
     *
     * @param string $title
     * @return Message
     */
    public function setTitle($title)
    {
        $this->title = $title;

        return $this;
    }

    /**
     * Get title
     *
     * @return string
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * Set content
     *
     * @param string $content
     * @return Message
     */
    public function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    /**
     * Get content
     *
     * @return string
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * Set date
     *
     * @param \DateTime $date
     * @return Message
     */
    public function setDate($date)
    {
        $this->date = $date;

        return $this;
    }

    /**
     * Get date
     *
     * @return \DateTime
     */
    public function getDate()
    {
        return $this->date;
    }

    /**
     * Set isSpam
     *
     * @param boolean $isSpam
     * @return Message
     */
    public function setIsSpam($isSpam)
    {
        $this->isSpam = $isSpam;

        return $this;
    }

    /**
     * Get isSpam
     *
     * @return boolean
     */
    public function getIsSpam()
    {
        return $this->isSpam;
    }

    /**
     * Set seenAt
     *
     * @param \DateTime $seenAt
     * @return Message
     */
    public function setSeenAt($seenAt)
    {
        $this->seenAt = $seenAt;

        return $this;
    }

    /**
     * Get seenAt
     *
     * @return \DateTime
     */
    public function getSeenAt()
    {
        return $this->seenAt;
    }

    /**
     * Set receiver
     *
     * @param \AppBundle\Entity\User $receiver
     * @return Message
     */
    public function setReceiver(\AppBundle\Entity\User $receiver = null)
    {
        $this->receiver = $receiver;

        return $this;
    }

    /**
     * Get receiver
     *
     * @return \AppBundle\Entity\User
     */
    public function getReceiver()
    {
        return $this->receiver;
    }

    /**
     * Set sender
     *
     * @param \AppBundle\Entity\User $sender
     * @return Message
     */
    public function setSender(\AppBundle\Entity\User $sender = null)
    {
        $this->sender = $sender;

        return $this;
    }

    /**
     * Get sender
     *
     * @return \AppBundle\Entity\User
     */
    public function getSender()
    {
        return $this->sender;
    }
}

Note the absence of any use statement regarding the ORM in the trait.

Doctrine will use the ODM namespace defined in your class.

This will produce two tables:

CREATE TABLE message (id INTEGER NOT NULL, receiver_id INTEGER DEFAULT NULL, sender_id INTEGER DEFAULT NULL, title VARCHAR(50) NOT NULL, content VARCHAR(2000) NOT NULL, date DATETIME NOT NULL, is_spam BOOLEAN NOT NULL, seen_at DATETIME DEFAULT NULL, PRIMARY KEY(id));
CREATE TABLE ticket (id INTEGER NOT NULL, receiver_id INTEGER DEFAULT NULL, sender_id INTEGER DEFAULT NULL, status VARCHAR(255) NOT NULL, title VARCHAR(50) NOT NULL, content VARCHAR(2000) NOT NULL, date DATETIME NOT NULL, is_spam BOOLEAN NOT NULL, seen_at DATETIME DEFAULT NULL, PRIMARY KEY(id));

Happy coding

Update for first example

in this example, if you search for messages using $em->getRepository('AcmePrivateMessageBundle:Message')->findAll(); , you will also get Ticket entities, because the created query looks like this:

SELECT 
  t0.id AS id2, 
  t0.title AS title3, 
  t0.content AS content4, 
  t0.date AS date5, 
  t0.is_spam AS is_spam6, 
  t0.seen_at AS seen_at7, 
  t0.receiver_id AS receiver_id8, 
  t0.sender_id AS sender_id9, 
  t0.discr, 
  t1.status AS status10 
FROM 
  message t0 
  LEFT JOIN ticket t1 ON t0.id = t1.id

(Note the LEFT JOIN )

But if you search Tickets with $entities = $em->getRepository('AcmePrivateTicketBundle:Ticket')->findAll(); you will find Ticket only because the generated sql looks a little different:

SELECT 
  t1.id AS id2, 
  t1.title AS title3, 
  t1.content AS content4, 
  t1.date AS date5, 
  t1.is_spam AS is_spam6, 
  t1.seen_at AS seen_at7, 
  t0.status AS status8, 
  t1.receiver_id AS receiver_id9, 
  t1.sender_id AS sender_id10, 
  t1.discr 
FROM 
  ticket t0 
  INNER JOIN message t1 ON t0.id = t1.id

To get messages only you have to use the query builder:

    $query = $em->createQuery("SELECT message FROM Acme\PrivateMessageBundle\Entity\Message message WHERE message INSTANCE OF Acme\PrivateMessageBundle\Entity\Message");
    $entities = $query->getResult();

This will produce this query:

SELECT 
  m0_.id AS id0, 
  m0_.title AS title1, 
  m0_.content AS content2, 
  m0_.date AS date3, 
  m0_.is_spam AS is_spam4, 
  m0_.seen_at AS seen_at5, 
  t1_.status AS status6, 
  m0_.discr AS discr7, 
  m0_.receiver_id AS receiver_id8, 
  m0_.sender_id AS sender_id9 
FROM 
  message m0_ 
  LEFT JOIN ticket t1_ ON m0_.id = t1_.id 
WHERE 
  m0_.discr IN ('message')

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