一对一的关系和复合形式(Symfony 3.4)

时间:2018-01-20 00:18:36

标签: php symfony

我有2个实体,练习和产品,他们有一对一的关系(拥有方是Product),我有一个ExerciseFormType和一个ProductFormType。

我希望我能够同时创建练习以及与之相关的产品。

我的想法是添加exerciseFormType的构建器,'product',它的类型将是ProductFormType。问题是产品需要exercise_id,并且练习没有持久化,所以它没有。这是我到目前为止所做的(它仅适用于在练习存在时创建产品)。

  <?php
    /**
     * exercise
     *
     * @ORM\Table(name="exercise")
     * @ORM\Entity(repositoryClass="SchoolBundle\Repository\ExerciseRepository")
     */
    class Exercise {
        /**
         * @var int
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;

        /**
         * @var string
         * @ORM\Column(name="name", type="string", length=255)
         */
        private $name;

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

        /**
         * @var string
         * @ORM\Column(name="language", type="string", length=255)
         */
        private $language;

        /**
         * @var int
         * @ORM\Column(name="level", type="integer")
         */
        private $level;

        /**
         * @var string
         * @ORM\Column(name="skills_required", type="string", length=255)
         */
        private $skillsRequired;

        /**
         * @var string
         * @ORM\Column(name="skills_targetted", type="string", length=255)
         */
        private $skillsTargetted;

        /**
         * @var string
         * @ORM\Column(name="tags", type="string", length=255)
         */
        private $tags;

        /**
         * @ORM\OneToMany(targetEntity="Session", mappedBy="exercise")
         */
        private $sessions;

        /**
         * @ORM\OneToMany(targetEntity="ExerciseIO", mappedBy="exercise")
         */
        private $exerciseIOs;

        /**
         * @ORM\OneToOne(targetEntity="MarketBundle\Entity\Product", mappedBy="exercise", cascade={"persist"}))
         */
        private $product;

        /**
         * @ORM\ManyToOne(targetEntity="Professor", inversedBy="createdExercises")
         * @ORM\JoinColumn(name="creator_id", referencedColumnName="id", nullable=false)
         */
        private $creator;

        /**
         * @ORM\ManyToMany(targetEntity="Professor", mappedBy="boughtExercises")
         */
        private $owners;

        public function __construct(){
            $this->sessions = new ArrayCollection();
            $this->exerciseIOs = new ArrayCollection();
            $this->owners = new ArrayCollection();
        }

        /**
         * Get creator
         * @return Professor
         */
        public function getCreator(){
            return $this->creator;
        }

        /**
         * Set creator
         * @param Professor $creator
         * @return Exercise
         */
        public function setCreator($creator){
            $this->creator = $creator;
            return $this;
        }

        /**
         * Get owner
         * @return ArrayCollection
         */
        public function getOwners(){
            return $this->owners;
        }

        /**
         * Get product
         * @return Product
         */
        public function getProduct(){
            return $this->product;
        }


        /**
         * @param Product $product
         * @return Exercise
         */
        public function setProduct($product){
            $this->product = $product;
            return $this;
        }

        /**
         * Get exerciseIOs
         * @return ArrayCollection
         */
        public function getExerciseIOs(){
            return $this->exerciseIOs;
        }

        /**
         * Get sessions
         * @return ArrayCollection
         */
        public function getSessions(){
            return $this->sessions;
        }

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

        /**
         * Set name
         * @param string $name
         * @return Exercise
         */
        public function setName($name){
            $this->name = $name;
            return $this;
        }

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

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

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

        /**
         * Set language
         * @param string $language
         * @return Exercise
         */
        public function setLanguage($language){
            $this->language = $language;
            return $this;
        }

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

        /**
         * Set level
         * @param string $level
         * @return Exercise
         */
        public function setLevel($level){
            $this->level = $level;
            return $this;
        }

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

        /**
         * Set skillsRequired
         * @param string $skillsRequired
         * @return Exercise
         */
        public function setSkillsRequired($skillsRequired){
            $this->skillsRequired = $skillsRequired;
            return $this;
        }

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

        /**
         * Set skillsTargetted
         * @param string $skillsTargetted
         * @return Exercise
         */
        public function setSkillsTargetted($skillsTargetted){
            $this->skillsTargetted = $skillsTargetted;
            return $this;
        }

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

        /**
         * Set tags
         * @param string $tags
         * @return Exercise
         */
        public function setTags($tags){
            $this->tags = $tags;
            return $this;
        }

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

在运动中我有那个

/**
 * Product
 *
 * @ORM\Table(name="product")
 * @ORM\Entity(repositoryClass="\MarketBundle\Repository\ProductRepository")
 */
class Product {

    /**
     * @var int
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var bool
     * @ORM\Column(name="visibility", type="boolean")
     */
    private $visibility;

    /**
     * @var float
     * @ORM\Column(name="price", type="float")
     */
    private $price;

    /**
     * @var \DateTime
     * @ORM\Column(name="publication_date", type="datetimetz")
     */
    private $publicationDate;

    /**
     * @var \DateTime
     * @ORM\Column(name="update_date", type="datetimetz", nullable=true)
     */
    private $updateDate;

    /**
     * @ORM\OneToMany(targetEntity="ProductComment", mappedBy="product")
     */
    private $productComments;

    /**
     * @ORM\OneToOne(targetEntity="\SchoolBundle\Entity\Exercise", inversedBy="product")
     * @ORM\JoinColumn(name="exercise_id", referencedColumnName="id", nullable=false)
     */
    private $exercise;

    public function __construct(){
        $this->productComments = new ArrayCollection();
    }

    /**
     * Get exercise
     * @return Exercise
     */
    public function getExercise(){
        return $this->exercise;
    }

    /**
     * Set exercise
     * @param Exercise $exercise
     * @return Product
     */
    public function setExercise($exercise){
        $this->exercise = $exercise;
        return $this;
    }



    /**
     * Get productComments
     * @return ArrayCollection
     */
    public function getProductComments(){
        return $this->productComments;
    }

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

    /**
     * Set visibility
     * @param boolean $visibility
     * @return Product
     */
    public function setVisibility($visibility){
        $this->visibility = $visibility;
        return $this;
    }

    /**
     * Get visibility
     * @return bool
     */
    public function getVisibility(){
        return $this->visibility;
    }

    /**
     * Set price
     * @param float $price
     * @return Product
     */
    public function setPrice($price){
        $this->price = $price;
        return $this;
    }

    /**
     * Get price
     * @return float
     */
    public function getPrice(){
        return $this->price;
    }

    /**
     * Set publicationDate
     * @param \DateTime $publicationDate
     * @return Product
     */
    public function setPublicationDate($publicationDate){
        $this->publicationDate = $publicationDate;
        return $this;
    }

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

    /**
     * Set updateDate
     * @param \DateTime $updateDate
     * @return Product
     */
    public function setUpdateDate($updateDate){
        $this->updateDate = $updateDate;
        return $this;
    }

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

在产品

public function newAction(Request $request){

        $exercise = new Exercise();
        $form = $this->createForm(ExerciseType::class, $exercise);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $exercise->setCreator($this->getUser());
            $em = $this->getDoctrine()->getManager();
            $em->persist($exercise);
            $em->flush();

            return $this->redirectToRoute('exercise_show', array('id' => $exercise->getId()));
        }

我说它适用于现有的练习(而且没有产品)。 当我尝试同时创建两者时

必须管理传递到选择字段的实体。也许坚持他们在实体经理?

它可能来自$ builder-&gt; getData(),因为在这种情况下我只传递给了一个“new Exercice()”。这是控制器动作

{{1}}

(另外这是另一个问题,但是当我禁用一个formType时,它会被渲染,并且值会出现但是它没有提交。我实际上更喜欢hiddenFormType但我需要DateTimeFormType的格式化)

2 个答案:

答案 0 :(得分:0)

好的,这是我用Symfony2.8做过的解决方案,我希望它可以帮到你。

/**
 * @ORM\Entity
 * @ORM\Table(name="picture")
 */
class Picture
{
    /**
     * @ORM\OneToOne(targetEntity="AppBundle\Entity\Host",
     *     inversedBy="pictureId"
     * )
     * @ORM\JoinColumn(name="avatar_host_id", referencedColumnName="id", nullable=true)
     */
    private $avatarHostId;

}
/**
 * @ORM\Entity
 * @ORM\Table(name="host")
 */
class Host
{

    /**
     * @ORM\OneToOne(targetEntity="AppBundle\Entity\Picture",
     *     mappedBy="avatarHostId", cascade={"persist", "remove", "merge"}
     * )
     */
    private $pictureId;
}
class HostType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('pictureId', PictureFileType::class); 
    }
}
class PictureFileType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('assetFile', FileType::class, array(
                'label' => 'Picture File',
                'required' => false,
                'validation_groups' => array('creation')
            ));
    }
}

答案 1 :(得分:0)

首先,我建议不要使用整数作为ID。它们周围存在一些问题,包括在这种情况下您无法随意生成一个问题。我建议Uuids along with ramsey/uuid,它也有Doctrine mapping

如果您遇到需要ID的情况,并且已生成Uuid,请自行生成Uuid。例如,在一个人为的例子中

class Order
{
    /**
     * @var UuidInterface
     *
     * @ORM\Id
     * @ORM\Column(type="uuid")
     * @ORM\GeneratedValue(strategy="NONE")
     * @ORM\CustomIdGenerator(class=UuidGenerator::class)
     */
    private $id;

    ...

    /**
     * Constructor.
     *
     * @param UuidInterface $id
     */
    public function __construct(?UuidInterface $id = null)
    {
        $this->id = $id ?? Uuid::uuid4();
    }

    ...
}

class OrderController
{
    ...

    public function orderProductAction(): JsonResponse
    {
        ...

        // We need this ahead of time, because
        // maybe this is Async and we need to
        // return the order id. This happens a
        // lot with commands.
        $generatedId = Uuid::uuid4();

        // Here it's going to use the given ID,
        // if not given, the Order class will
        // generate it.
        $order = new Order($generatedId, ...);

        // Stuff happens, persisted... maybe we
        // don't have the persisted Order object...

        return $this->json([
            'order' => [
                'id' => $generatedId->toString(),
            ],
        ]);
    }

    ...
}

它的主要内容是在构造函数中提供生成的Uuid(如果null,它会交替生成它)。 添加setter。这是一个更为复杂的例子:

public function createAction(Request $request): JsonResponse
{
    $request_data = \json_decode($request->getContent(), true);
    $tenant_data = $request_data['data'];

    // Pre-generate our Uuid for the Tenant.
    $generated_id = Uuid::uuid4();

    /** @var CreateTenant $create_tenant_cmd */
    $create_tenant_cmd = new CreateTenant($generated_id);

    $create_tenant_cmd->setName($tenant_data['name']);
    $create_tenant_cmd->setDomainName($tenant_data['domain_name']);
    $create_tenant_cmd->setOwnerUsername($tenant_data['owner_username']);
    $create_tenant_cmd->setOwnerPassword($tenant_data['owner_password']);
    $create_tenant_cmd->setOwnerFirstName($tenant_data['owner_first_name']);
    $create_tenant_cmd->setOwnerLastName($tenant_data['owner_last_name']);
    $create_tenant_cmd->setOwnerEmail($tenant_data['owner_email']);

    if (!$this->isGranted('perform', $create_tenant_cmd)) {
        throw new AccessDeniedException('Access denied to create tenant.');
    }

    // Here, we should expect NO side-effects, the command
    // can't return anything. So no Tenant object with 
    // generated Uuid...
    $this->get('command_bus')->handle($create_tenant_cmd);

    /* @var Serializer $serializer */
    $serializer = $this->get('jms_serializer');
    $serializer_context = SerializationContext::create()->setGroups([
        'tenant_meta',
    ]);

    $tenant = $this->get('app.repository.tenant')->find($generated_id);

    return $this->json(\json_decode($serializer->serialize(
        $tenant,
        'json',
        $serializer_context
    )));
}

这是ID问题;您的问题的第二部分现在很容易解决,即Uuid代替FormType

$form = $this->createForm(OrderProductType::class, $product, [
    'product' => $product,
    'generatedId' => $generatedId,
]);

class OrderProductType extends AbstractType implements DataMapperInterface
{
    /** @var UuidInterface */
    protected $generatedId;

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $this->generatedId = $options['generatedId'];
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver
            ->setDefaults([
                'data_class' => OrderProduct::class,
            ])
            ->setRequired(['generatedId'])
        ;
    }

    public function mapFormsToData($forms, &$data)
    {
        $forms = iterator_to_array($forms);

        $data = new OrderProduct($this->generatedId);

        ...
    }

}

当然,如果您需要它作为字段,请将其与您选择的字段类型一起使用,而不是将其存储为变量,然后从该字段中获取它:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('product_id', HiddenType::class, [
            'product_id' => $options['generatedId']->toString(),
        ])
        ...
    ;
}

public function mapFormsToData($forms, &$data)
{
    $forms = iterator_to_array($forms);

    // Note use of Uuid::fromString().
    $data = new OrderProduct(Uuid::fromString(
        $forms['productId']->getData()
    ));

    ...
}

这是不将交互的进程委托给委托对象(如命令)而不是生成实体的缺点之一;在你需要它之​​前,你需要整体构建你的对象,或者在设计中使它变得合适。它也使得断言的处理更加困难(你只得到一套,实体&#39;)。