Генерация схемы для отношения многие к одному

1

У меня есть класс Message и вы хотите расширить класс Ticket, который будет представлять собой класс билетов на поддержку, поэтому у них может быть, например, другое поле, называемое "статус".

Родительский класс:

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
}

Как вы можете видеть, у него есть много к одному отношения с моим классом User в приемнике полей и отправителе. Этот класс генерируется просто отлично.

Класс child, который я хочу расширить из класса Message:

    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
}

Есть несколько проблем с этим классом. Он был пуст, но я сгенерировал поля и функции с помощью doctrine: generate: entities SupportMessageBundle.

Во-первых, он генерирует поле private, а в схеме: update я получаю

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

Поэтому я изменяю все поля на protected и генерирует мои таблицы в базе данных, но без идентификатора отправителя и получателя. Любые идеи, как я могу заставить это сделать это тоже? Или почему мои поля сделаны частными в первую очередь?

Обратите внимание, что я хочу, чтобы у Ticket все еще были поля Message, я не хочу, чтобы некоторые из моих сообщений были билетами.

  • 0
    Я думаю, что стоит посмотреть на doctrine-orm.readthedocs.org/en/latest/reference/…, чтобы узнать, как Doctrine поддерживает наследование.
  • 0
    Я бы хотел пример Symfony, который меня немного смущает.
Показать ещё 1 комментарий
Теги:
doctrine

2 ответа

1
Лучший ответ

Это всегда зависит от того, что вы хотите спроектировать. Прежде всего, скажем, что генератор не работает для вашего предложения, поэтому я рекомендую, а не использовать его в этом случае.

Что-то я изменил:

  • Я использую два пакета PrivateMessageBundle и PrivateTicketBundle как в пространстве имен Acme
  • Я изменил свойства $ is_spam и $ seen_at на $ isSeen и $ seenAt из-за стиля кода symfony

Отображение наследования

Правильное определение класса, если вы хотите использовать наследование:

<?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
 }

И класс Ticket выглядит так

<?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;
    }
}

это приведет к созданию следующих таблиц:

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));

Один для объекта сообщения со всеми полями, определенными для сообщения, и таблицей для билета только с идентификатором и идентификатором (который всегда будет совпадать с соответствующим идентификатором в таблице сообщений) и дополнительным статусом поля.

Две таблицы с одинаковыми столбцами

Это также возможно с использованием признаков: Сущность сообщения:

<?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;
}

(Вы должны use Doctrine\ORM\Mapping as ORM;)

Билет:

<?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;
    }

}

И вот Трейт:

<?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;
    }
}

Обратите внимание на отсутствие каких-либо утверждений о применении ORM в признаке.

Doctrine будет использовать пространство имен ODM, определенное в вашем классе.

Это создаст две таблицы:

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));

Счастливое кодирование

Обновление для первого примера

в этом примере, если вы ищете сообщения с помощью $em->getRepository('AcmePrivateMessageBundle:Message')->findAll(); , вы также получите объекты Билета, потому что созданный запрос выглядит так:

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

(Обратите внимание на LEFT JOIN)

Но если вы ищете Билеты с $entities = $em->getRepository('AcmePrivateTicketBundle:Ticket')->findAll(); вы найдете Ticket только потому, что сгенерированный sql выглядит немного иначе:

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

Чтобы получать сообщения, вы должны использовать построитель запросов:

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

Это вызовет этот запрос:

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')
  • 0
    Я попробую второй пример, кажется, это то, что я ищу. Но можете ли вы объяснить в своем первом примере, почему у Ticket есть только поля id и status? Я получил то же самое, и я не понимаю. Это сделать некоторые из моих сообщений в качестве билетов? Или что мне здесь не хватает? Потому что, если я хочу сделать Билет, сначала у меня должно быть Сообщение, чтобы я мог связать его с идентификатором.
  • 0
    Вовсе нет, если вы создаете заявку, вы также создаете сообщение. Например: $messageRepo->findAll() также возвращает сущности $messageRepo->findAll() . Но $ticketRepo->findAll() просто вернет билеты. Вы можете легко проверить это, если создадите неуклюжий crontroller.
Показать ещё 3 комментария
1

Что-то подобное сделает это. Но вы должны следовать комментарию @StuBez и читать https://doctrine-orm.readthedocs.org/en/latest/reference/inheritance-mapping.html

Это СОЕДИНЕННЫЙ пример, который имеет производительность в действии в определенном случае использования.

<?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
}
  • 0
    Я помещаю класс Ticket в отдельный пакет, а не в тот же пакет или файл, верно? В любом случае, результат тот же. Все поля все еще являются закрытыми в классе Ticket, и таблица в базе данных, сгенерированная после их изменения вручную на защищенную, теперь не имеет всего. Он содержит только идентификатор.
  • 0
    Таким образом, это позволяет мне создавать «Сообщения» типа «Билет», но я хочу иметь «Билеты» с такими же полями, как «Сообщения», и с дополнительными полями.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню