【发布时间】:2014-04-12 09:18:00
【问题描述】:
我有 3 个实体:公司、行业、类别 我想创建一个表单,用户可以在其中输入公司名称,然后从下拉列表中选择行业。每个行业都有类别。当用户选择一个行业时,我想填充类别列表。我读过以下文章:http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data
我已经创建了表单,但是当触发 ajax 调用时出现以下错误:
Neither the property "categories" nor one of the methods "setCategories()", "__set()" or "__call()" exist and have public access in class "ebulucu\MainBundle\Entity\Company"
我现在已经很多天了,只是无法让它工作。我希望有人能给我一些提示。我需要一个带有公司名称输入字段的表单,两个下拉列表行业和类别,其中类别取决于所选行业。 Company 与 Category 有 ManyToMany 关系, Industry 也有 OneToMany 关系。到目前为止,这是我的代码:
编辑: 我已经尝试过使用 OneToMany 而不是 Company 和 Category 之间的 ManyToMany 关系的代码。 这很好用。但是万一ManyToMany Relation怎么办?如何管理加载和设置类别?
我的 3 个实体:
class Company
{
/**
* @var integer
* @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;
/**
* @ORM\ManyToMany(targetEntity="Category", mappedBy="companies")
*/
private $categories;
/**
* Constructor
*/
public function __construct()
{
$this->categories = new \Doctrine\Common\Collections\ArrayCollection();
}
... setters and getters
class Industry
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=80)
* @Assert\NotBlank()
*/
private $name;
/**
* @var
*
* @ORM\OneToMany(targetEntity="Category",mappedBy="industry")
*/
private $categories;
public function __construct()
{
$this->categories = new ArrayCollection();
}
...setters and getters
class Category
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=80)
* @Assert\NotBlank()
*/
private $name;
/**
* @ORM\ManyToOne(targetEntity="Industry", inversedBy="categories");
* @ORM\JoinColumn(name="industry_id", referencedColumnName="id",nullable=false)
*/
private $industry;
/**
* @ORM\ManyToMany(targetEntity="Company", inversedBy="categories");
* @ORM\JoinTable(name="categories_companies")
*/
private $companies;
... setters and getters
我的公司表格类:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use ebulucu\MainBundle\Entity\Industry;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Doctrine\ORM\EntityRepository;
class CompanyRegistrationFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
$builder->add('industry', 'entity', array(
'mapped' => false,
'class' => 'ebulucuMainBundle:Industry',
'property' => 'name',
'empty_value' => 'Choose industry',
));
$formModifier = function(FormInterface $form, $industry_id) {
if($industry_id) {
$form->add('categories', 'entity', array(
'class' => 'ebulucuMainBundle:Category',
'query_builder' => function(EntityRepository $er) use ($industry_id) {
$query = $er->createQueryBuilder('i')
->select(array('i'))
->where('i.industry_id = :industry_id')
->setParameter('industry_id', $industry_id)
->orderBy('i.name', 'ASC');
return $query;
},
'empty_value' => 'Choose category'
)
);
}
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event) use ($formModifier) {
$formModifier($event->getForm(), null);
}
);
//** Checks for Industry that is submitted and adds categories based on industry selection **//
$builder->get('industry')->addEventListener(
FormEvents::POST_SUBMIT, function(FormEvent $event) use ($formModifier) {
$industry_id = $event->getData();
$formModifier($event->getForm()->getParent(), $industry_id);
}
);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'ebulucu\MainBundle\Entity\Company',
));
}
public function getName()
{
return 'company';
}
}
控制器:
use ebulucu\MainBundle\Entity\Company;
use ebulucu\MainBundle\Form\Type\CompanyRegistrationFormType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use ebulucu\MainBundle\Entity\Industry;
use ebulucu\MainBundle\Entity\Category;
use Symfony\Component\HttpFoundation\Request;
class MainController extends Controller
{
/**
* @Route("/", name="homepage")
* @Template()
*/
public function indexAction(Request $request)
{
$company = new Company();
$form = $this->createForm(new CompanyRegistrationFormType(), $company);
$form->handleRequest($request);
if ($form->isValid()) {
return $this->redirect($this->generateUrl('fos_user_security_login'));
}
return array(
'form' => $form->createView(),
);
}
/**
* @Route("/", name="loadIndustryCategories")
* @Template()
*/
public function loadIndustryCategories(Request $request)
{
$company = new Company();
$form = $this->createForm(new CompanyRegistrationFormType(), $company);
$form->handleRequest($request);
return array(
'form' => $form->createView(),
);
}
}
带有表单和ajax调用的Twig模板:
{% block content %}
<div>Homepage</div>
{{ form_start(form, {'attr': {'id': 'form_industry'}}) }}
{{ form_end(form) }}
{% endblock %}
{% block js%}
<script>
$('#company_industry').change( function() {
var postData = $("#form_industry").serializeArray();
var formURL = {{ path('loadIndustryCategories') }};
$.ajax(
{
url : formURL,
type: "POST",
data : postData,
success:function(data, textStatus, jqXHR)
{
//data: return data from server
},
error: function(jqXHR, textStatus, errorThrown)
{
//if fails
}
});
e.preventDefault(); //STOP default action
e.unbind(); //unbind. to stop multiple form submit.
});
</script>
{% endblock %}
【问题讨论】:
-
您的公司实体中有 setCategories 方法吗?
-
不,我有 add-、remove- 和 getCategories
-
您的公司实体中需要一个 setCategories 函数。表单处理程序基本上会为您编译集合,然后尝试将其分配给实体字段。
-
现在我已将公司和类别之间的关系从 ManyToMany 更改为 OneToMany,并且代码确实有效。但是我如何使用 ManyToMany Relation 和 arrayCollection 来管理呢?
标签: ajax forms symfony dynamic