Symfony2:保存集合实体混淆



免责声明:我是Symfony的新手。真的很难处理集合字段类型和OnetoOne关系的简单设置。

场景:我有一个Product实体类和一个Category实体类。我在产品上使用一个集合字段来创建类别项。我正在描绘一个单独的类别表与名称列和相关的product_id列。我知道这并不实用。为了便于讨论,类别也可以是功能或其他东西,因为我只是想将关系建立为要扩展的场景。最终,类别将成为一个图像字段,允许我将相关图像拉入视图。

问题:我一遍又一遍地看那篇食谱文章(http://symfony.com/doc/current/cookbook/form/form_collections.html),但总是碰壁。我同意这个原则,但我觉得我错过了一些重要的东西。我有一个原型表单字段从javascript生成,我成功地持久化/保存新产品(全部)和新类别(仅部分)。相关的产品id没有写入连接列。

我确信这是getter/setter没有被正确处理的情况。我依靠理论来自动生成它们。或者问题可能是一些未指定的要求,将id设置为控制器中的Category。

接下来的代码。帮助非常感激,因为一直在敲打这个周围几天,没有得到快速。我真的很沮丧,因为我很快就掌握了所有其他原则,并且在symfony中构建了第一个项目。

产品实体

<?php 
namespace AppBundleEntity;
use DoctrineORMMapping as ORM;
use SymfonyComponentValidatorConstraints as Assert;
use SymfonyComponentHttpFoundationFileFile;
use VichUploaderBundleMappingAnnotation as Vich;
use DoctrineCommonCollectionsArrayCollection;
use AppBundleEntityCategory;
/**
 * Page
 *
 * @ORMTable(name="product")
 * @ORMEntity
 * @VichUploadable
 */
class Product
{
    /**
     * @var integer
     *
     * @ORMColumn(name="id", type="integer")
     * @ORMId
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @var string
     *
     * @ORMColumn(name="Title", type="string", length=255)
     * @AssertNotBlank()
     */
    private $title;
/**
 * Get id
 *
 * @return integer 
 */
public function getId()
{
    return $this->id;
}
    /**
   * @ORMOnetoOne(targetEntity="Category", cascade={"persist"})
   */
    protected $categorys;
    public function __construct()
    {
        $this->categorys = new ArrayCollection();
    }
}

类别实体

<?php
namespace AppBundleEntity;
use DoctrineORMMapping as ORM;
use AppBundleEntityProduct;
/**
 * Category
 *
 * @ORMTable(name="category")
 * @ORMEntity
 */
class Category
{
    /**
     * @var integer
     *
     * @ORMColumn(name="id", type="integer")
     * @ORMId
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @var string
     *
     * @ORMColumn(name="name", type="string", length=255)
     */
    private $name;
    /**
     * @ORMOneToOne(targetEntity="Product", cascade={"persist"})
     */
     protected $product;
    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }
    /**
     * Set name
     *
     * @param string $name
     * @return Category
     */
    public function setName($name)
    {
        $this->name = $name;
        return $this;
    }
    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set product
     *
     * @param AppBundleEntityProduct $product
     * @return Category
     */
    public function setProduct(AppBundleEntityProduct $product = null)
    {
        $this->product = $product;
        return $this;
    }
    /**
     * Get product
     *
     * @return AppBundleEntityProduct 
     */
    public function getProduct()
    {
        return $this->product;
    }
}

产品类型

<?php 
namespace AppBundleFormType;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
use AppBundleEntityProduct;
use AppBundleEntityCategory;
class ProductType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder  
            ->add('title', 'text')
             ->add('categorys', 'collection', array(
              'type' => new CategoryType(),
              'allow_add' => true,
              'by_reference' => false,
              )) 
              ->add('save', 'submit', array(
                  'attr' => array('class' => 'btn btn-default'),
            ))
      ;

    }
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundleEntityProduct',
        ));
    }
    public function getName()
    {
        return 'product';
    }
}

类别类型

<?php
namespace AppBundleFormType;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
use AppBundleEntityCategory;

class CategoryType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name', 'text');
    }
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundleEntityCategory',
        ));
    }
    public function getName()
    {
        return 'category';
    }
}

产品控制器:新产品

  /**
   * @Route("admin/product/new", name="product_add")
   * @Security("has_role('ROLE_ADMIN')")
   */
  public function newAction(Request $request)
  {
    $product = new Product();
    $form = $this->createForm(new ProductType(), $product);
    $category = new Category();
    $form->handleRequest($request);
    if ($form->isValid()) {
        $category->getProduct($this);
        $em = $this->getDoctrine()->getManager();
        $em->persist($product);
        $em->flush();
        return $this->redirectToRoute('products_admin');
    } 
    return $this->render('Product/productAdd.html.twig', array(
        'form' => $form->createView(),
    ));        
  }

我敢打赌,你不需要OneToOne,而是OneToMany的产品与类别的关系。因为我认为一个类别可以有多个产品,但一个产品只能有一个类别。

另一种方法是创建多多关系。在这种情况下,每个类别可以有多个产品,但每个产品也可以在多个类别中。

OneToOne不经常使用,只在特殊情况下使用。

我认为在您的情况下也不需要集合类型。您可以为实体类型更改这一点。就像我解释的那样,把实体设为ManyToOne。

namespace AppBundleEntity;
use DoctrineORMMapping as ORM;
/**
 * Product
 *
 * @ORMTable()
 * @ORMEntity
 */
class Product
{
    /**
     * @var integer
     *
     * @ORMColumn(name="id", type="integer")
     * @ORMId
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @var string
     *
     * @ORMColumn(name="description", type="string", length=64)
     */
    private $description;
    /**
     * @var float
     *
     * @ORMColumn(name="price", type="float")
     */
    private $price;
    /**
     * @ORMManyToOne(targetEntity="Category")
     * @ORMJoinColumn(name="category_id", referencedColumnName="id")
     **/
    private $category;
}

和类别:

namespace AppBundleEntity;
use DoctrineORMMapping as ORM;
/**
 * Category
 *
 * @ORMTable()
 * @ORMEntity
 */
class Category
{
    /**
     * @var integer
     *
     * @ORMColumn(name="id", type="integer")
     * @ORMId
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @var string
     *
     * @ORMColumn(name="name", type="string", length=64)
     */
    private $name;
    public function __toString() {
        return $this->name;
    }
}

,现在使用控制台运行一些命令:

app/console doctrine:generate:entities AppBundle
app/console doctrine:schema:update --force
app/console doctrine:generate:crud AppBundle:Product
app/console doctrine:generate:crud AppBundle:Category

如果被问及是否要生成写操作,请选择YES

添加__toString()方法到你的Category实体:

public function __toString() {
    return $this->name;
}

现在看看你的新控制器和路由,并尝试它们。

查看所有路由:

app/console router:debug

你必须在app/config/routing.yml:

app:
    resource: "@AppBundle/Controller/"
    type:     annotation

结尾是:

- two new entities
- two new controllers
- two new form types
- eight new views

您可以从创建的代码中学到很多东西,并按您的意愿更改所有内容。祝你好运

我知道这是个老问题,但是:

/**
   * @Route("admin/product/new", name="product_add")
   * @Security("has_role('ROLE_ADMIN')")
   */
  public function newAction(Request $request)
  {
    $product = new Product();
    $form = $this->createForm(new ProductType(), $product);
    $category = new Category();
    $form->handleRequest($request);
    if ($form->isValid()) {
        #################################
        ##$category->getProduct($this);##
        #################################
        $category->setProduct($this);
        $em = $this->getDoctrine()->getManager();
        $em->persist($product);
        $em->flush();
        return $this->redirectToRoute('products_admin');
    } 
    return $this->render('Product/productAdd.html.twig', array(
        'form' => $form->createView(),
    ));        
  }

调用getProduct($this)而不是setProduct($this)

最新更新