未能完成1到n的关系

时间:2014-06-16 20:24:06

标签: symfony orm doctrine-orm

我需要有人来帮助我完成我的工作,这对许多人来说可能很简单但对我来说很复杂因为我还在学习symfony。我想在品牌汽车之间建立一对一的关系,这样一个品牌在逻辑上可以有很多车。

我有一个表单可以插入新的brand,但是我还有另一个表单可以从选择框中选择car插入brand,这是我正在努力的地方。我可以使用db中的当前brands填充选择框。当持久化car时,我得到一个错误(期望一个对象不是字符串),并且不知道如何解决它。

我需要你的帮助来修改下面的代码,以便我可以坚持使用。

提前致谢

enter image description here

/var/www/html/local/sym/src/Car/BrandBundle/Resources/config/routing.yml

home:
    pattern:  /
    defaults: { _controller: CarBrandBundle:Home:index }
    methods:  [GET]

brand:
    pattern:  /brand
    defaults: { _controller: CarBrandBundle:Brand:index }
    methods:  [GET]

brandCreate:
    pattern:  /brand/create
    defaults: { _controller: CarBrandBundle:Brand:create }
    methods:  [POST]

car:
    pattern:  /car
    defaults: { _controller: CarBrandBundle:Car:index }
    methods:  [GET]

carCreate:
    pattern:  /car/create
    defaults: { _controller: CarBrandBundle:Car:create }
    methods:  [POST]

BrandEntity.php

namespace Car\BrandBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * Class BrandEntity
 *
 * @ORM\Entity
 * @ORM\Table(name = "brands")
 * @UniqueEntity(fields = "name", message = "The Brand already exist in database.")
 * @package Car\BrandBundle\Entity
 */
class BrandEntity
{
    /**
     * @ORM\Id
     * @ORM\Column(type = "smallint")
     * @ORM\GeneratedValue(strategy = "AUTO")
     * @var int $id
     */
    protected $id;

    /**
     * @Assert\NotBlank(message = "The Name field should not be blank")
     * @Assert\Length(max = 20, maxMessage = "The Name field cannot be longer than {{ limit }} characters length.")
     * @ORM\Column(type = "string", length = 20)
     * @var string $name
     */
    protected $name;

    /**
     * @ORM\OneToMany(targetEntity = "CarEntity", mappedBy = "brand")
     * @var object $car
     */
    protected $car;

    /**
     * Constructor.
     */
    public function __construct()
    {
        $this->car = new ArrayCollection();
    }

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

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

        return $this;
    }

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

    /**
     * Add car
     *
     * @param \Car\BrandBundle\Entity\CarEntity $car
     * @return BrandEntity
     */
    public function addCar(\Car\BrandBundle\Entity\CarEntity $car)
    {
        $this->car[] = $car;

        return $this;
    }

    /**
     * Remove car
     *
     * @param \Car\BrandBundle\Entity\CarEntity $car
     */
    public function removeCar(\Car\BrandBundle\Entity\CarEntity $car)
    {
        $this->car->removeElement($car);
    }

    /**
     * Get car
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getCar()
    {
        return $this->car;
    }
}

BrandType.php

namespace Car\BrandBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

/**
 * Class BrandType
 * @package Car\BrandBundle\Form\Type
 */
class BrandType extends AbstractType
{
    /**
     * Returns the db mapped form object.
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->setAction($options['action'])
            ->setMethod('POST')
            ->add('name', 'text', array('label' => 'Name'))
            ->add('button', 'submit', array('label' => 'Add'))
        ;
    }

    /**
     * Returns relevant data class to the form.
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array('data_class' => 'Car\BrandBundle\Entity\BrandEntity'));
    }

    /**
     * Returns the name of the form.
     * @return string
     */
    public function getName()
    {
        return 'brand';
    }
} 

BrandController.php

namespace Car\BrandBundle\Controller;

use Car\BrandBundle\Entity\BrandEntity;
use Car\BrandBundle\Form\Type\BrandType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * Class BrandController.
 * Handles brand.
 * @package Car\BrandBundle\Controller
 */
class BrandController extends Controller
{
    /**
     * Generates landing web page.
     * @return Response
     */
    public function indexAction()
    {
        $form = $this->getFrom();

        return $this->render('CarBrandBundle:Default:brand.html.twig',
                array('page' => 'Brand', 'form' => $form->createView(), 'brands' => $this->getBrands()));
    }

    /**
     * Handles form submission.
     * @param Request $request
     * @return Response
     */
    public function createAction(Request $request)
    {
        if ($request->getMethod() != 'POST')
        {
            return new Response('Only POST method is allowed');
        }

        $form = $this->getFrom();

        $form->handleRequest($request);

        if ($form->isValid())
        {
            $submission = $form->getData();

            $em = $this->getDoctrine()->getManager();

            $brand = new BrandEntity();
            $brand->setName($submission->getName());

            $em->persist($brand);
            $em->flush();

            $this->redirect($this->generateUrl('brand'));
        }

        return $this->render('CarBrandBundle:Default:brand.html.twig',
                array('page' => 'Brand', 'form' => $form->createView(), 'brands' => $this->getBrands()));
    }

    /**
     * Returns web form.
     * @return \Symfony\Component\Form\Form
     */
    private function getFrom()
    {
        return $this->createForm(new BrandType(), new BrandEntity(),
                array('action' => $this->generateUrl('brandCreate')));
    }

    /**
     * Return brands from db.
     * @return array
     */
    private function getBrands()
    {
        $repo = $this->getDoctrine()->getRepository('CarBrandBundle:BrandEntity');
        $brands = $repo->findAll();

        return $brands;
    }
} 

brand.html.twig

{% extends '::base.html.twig' %}

{% block body %}
    {{ form_start(form, { attr: {novalidate: 'novalidate'} }) }}
        {% if not form.vars.valid %}
            <div class="global_form_errors">
                Form has errors!
            </div><br />
        {% endif %}

        <div>
            {{ form_label(form.name) }}

            {% if form.name.vars.errors|length != '' %}
                {{ form_widget(form.name, { attr: {'class': 'individual_form_error'} }) }}
            {% else %}
                {{ form_widget(form.name) }}
            {% endif %}

            {{ form_errors(form.name) }}
        </div>

        <div>
            {{ form_widget(form.button) }}
        </div>

    {{ form_end(form) }}

    <hr />
    Total Brands: {{ brands|length }}
    <br />
    {% if brands|length != 0 %}
        {% set i = 1 %}
        {% for brand in brands %}
            {{ '(' ~ i ~ ') - ' ~ brand.id ~ ' - ' ~ brand.name }}<br />
            {% set i = i+1 %}
        {% endfor %}
    {% endif %}
{% endblock %}

CarEntity.php

namespace Car\BrandBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Class CarEntity.
 * @ORM\Entity
 * @ORM\Table(name = "cars", uniqueConstraints={@ORM\UniqueConstraint(columns={"model", "brand_id"})})
 * @package Car\BrandBundle\Entity
 */
class CarEntity
{
    /**
     * @ORM\Id
     * @ORM\Column(type = "smallint")
     * @ORM\GeneratedValue(strategy = "AUTO")
     * @var int $id
     */
    protected $id;

    /**
     * @Assert\NotBlank(message = "The Model should not be blank.")
     * @Assert\Length(max = 20, maxMessage = "The Brand field cannot be longer than {{ limit }} characters length.")
     * @ORM\Column(type = "string", length = 20)
     * @var string $model
     */
    protected $model;

    /**
     * @Assert\NotBlank(message = "The Brand should not be blank.")
     * @ORM\ManyToOne(targetEntity="BrandEntity", inversedBy="car")
     * @ORM\JoinColumn(name="brand_id", referencedColumnName="id", nullable=false)
     * @var object $brand
     */
    protected $brand;

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

    /**
     * Set model
     *
     * @param string $model
     * @return CarEntity
     */
    public function setModel($model)
    {
        $this->model = $model;

        return $this;
    }

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

    /**
     * Set brand
     *
     * @param \Car\BrandBundle\Entity\BrandEntity $brand
     * @return CarEntity
     */
    public function setBrand(\Car\BrandBundle\Entity\BrandEntity $brand = null)
    {
        $this->brand = $brand;

        return $this;
    }

    /**
     * Get brand
     *
     * @return \Car\BrandBundle\Entity\BrandEntity 
     */
    public function getBrand()
    {
        return $this->brand;
    }
}

CarType.php

namespace Car\BrandBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

/**
 * Class CarType
 * @package Car\BrandBundle\Form\Type
 */
class CarType extends AbstractType
{
    /**
     * @var array $brands
     */
    private $brands;

    /**
     * Constructor.
     * @param array $brands
     */
    public function __construct(array $brands)
    {
        $this->brands = $brands;
    }

    /**
     * Returns web form object.
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->setAction($options['action'])
            ->setMethod('POST')
            ->add('brand', 'choice', array('label' => 'Brand', 'choices' => $this->getBrands()))
            ->add('model', 'text', array('label' => 'Model'))
            ->add('button', 'submit', array('label' => 'Add'))
        ;
    }

    /**
     * Returns relevant data class to the form.
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array('data_class' => 'Car\BrandBundle\Entity\CarEntity'));
    }

    /**
     * Returns name of the web form.
     * @return string
     */
    public function getName()
    {
        return 'car';
    }

    /**
     * Returns choices for select box
     * @return array
     */
    public function getBrands()
    {
        $choices = array('' => '');

        foreach ($this->brands as $brand)
        {
            $choices[$brand->getId()] = $brand->getName();
        }

        return $choices;
    }
} 

CarController.php

namespace Car\BrandBundle\Controller;

use Car\BrandBundle\Entity\CarEntity;
use Car\BrandBundle\Form\Type\CarType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * Class CarController
 * @package Car\BrandBundle\Controller
 */
class CarController extends Controller
{
    /**
     * Generates landing web page.
     * @return Response
     */
    public function indexAction()
    {
        $form = $this->getForm();

        return $this->render('CarBrandBundle:Default:car.html.twig',
                array('page' => 'Car', 'form' => $form->createView(), 'cars' => $this->getCars()));
    }

    public function createAction(Request $request)
    {
        if ($request->getMethod() != 'POST')
        {
            return new Response('Only POST method is allowed');
        }

        $form = $this->getForm();

        $form->handleRequest($request);

        if ($form->isValid())
        {
            $submission = $form->getData();

            echo '<pre>'; print_r($submission); exit;
            //$brand = new BrandEntity();

        }

        return $this->render('CarBrandBundle:Default:car.html.twig',
                array('page' => 'Car', 'form' => $form->createView(), 'cars' => $this->getCars()));
    }

    /**
     * Returns web form.
     * @return \Symfony\Component\Form\Form
     */
    private function getForm()
    {
        return $this->createForm(new CarType($this->getBrands()), new CarEntity(),
                array('action' => $this->generateUrl('carCreate')));
    }

    /**
     * Return cars from db.
     * @return array
     */
    private function getCars()
    {
        $repo = $this->getDoctrine()->getRepository('CarBrandBundle:CarEntity');
        $cars = $repo->findAll();

        return $cars;
    }

    /**
     * Return brands from db.
     * @return array
     */
    private function getBrands()
    {
        $repo = $this->getDoctrine()->getRepository('CarBrandBundle:BrandEntity');
        $brands = $repo->findAll();

        return $brands;
    }
} 

car.html.twig

{% extends '::base.html.twig' %}

{% block body %}
    {{ form_start(form, { attr: {novalidate: 'novalidate'} }) }}
    {% if not form.vars.valid %}
        <div class="global_form_errors">
            Form has errors!
        </div><br />
    {% endif %}

    <div>
        {{ form_label(form.brand) }}

        {% if form.brand.vars.errors|length != '' %}
            {{ form_widget(form.brand, { attr: {'class': 'individual_form_error'} }) }}
        {% else %}
            {{ form_widget(form.brand) }}
        {% endif %}

        {{ form_errors(form.brand) }}
    </div>

    <div>
        {{ form_label(form.model) }}

        {% if form.model.vars.errors|length != '' %}
            {{ form_widget(form.model, { attr: {'class': 'individual_form_error'} }) }}
        {% else %}
            {{ form_widget(form.model) }}
        {% endif %}

        {{ form_errors(form.model) }}
    </div>

    <div>
        {{ form_widget(form.button) }}
    </div>

    {{ form_end(form) }}

    <hr />
    Total Cars: {{ cars|length }}
    <br />
    {% if cars|length != 0 %}
        {% set i = 1 %}
        {% for car in cars %}
            {{ '(' ~ i ~ ') - ' ~ car.id ~ ' - ' ~ car.model }}<br />
            {% set i = i+1 %}
        {% endfor %}
    {% endif %}
{% endblock %}

1 个答案:

答案 0 :(得分:2)

您应该将品牌选择从简单的选择框更改为实体选择,如此......

/**
 * Returns web form object.
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->setAction($options['action'])
        ->setMethod('POST')
        ->add('users', 'entity', array(
                               // Entity class
            'class'         => 'CarBrandBundle:BrandEntity',
                               // Field displayed in select
            'property'      => 'name',
                               // Closure with repository query
            'query_builder' => function(EntityRepository $repo) {
                return $repo->createQueryBuilder('b')
                    ->orderBy('b.name', 'ASC');
            },
            'multiple'      => false,
            'expanded'      => false,
        ))
        ->add('model', 'text', array('label' => 'Model'))
        ->add('button', 'submit', array('label' => 'Add'))
    ;
}

这会将您的表单填充为与实际品牌对象相关的选择框,而不是您当前生成的数组键。