Мне не удалось найти какое-либо решение, соответствующее моему сценарию, поэтому я решил спросить здесь: в основном, мне нужно добиться, чтобы отобразить форму с несколькими полями выбора, а именно Company, ProductsCategory и Products. Поэтому, в зависимости от того, какую категорию выбирает пользователь, я хочу фильтровать и показывать только продукты этой выбранной категории. Я пытался следовать документации Symfony , как указано здесь , но не могу заставить ее работать. поле выбора интерфейсных продуктов остается пустым даже после того, как категория была задана, а также ajax возврат со статусом 500 с ошибкой:
Возвращаемое значение App \ Entity \ ProductsCategory :: getProducts ( ) должен быть экземпляром App \ Entity \ Products или null, возвращен экземпляр Doctrine \ ORM \ PersistentCollection
вот коды: сущность My Exportables
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\ExportablesRepository")
*/
class Exportables
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\ExportCompany", inversedBy="exportables")
* @ORM\JoinColumn(nullable=false)
*/
private $company;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Products", inversedBy="exportables")
* @ORM\JoinColumn(nullable=false)
*/
private $product;
/**
* @ORM\Column(type="boolean")
*/
private $isActive;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\ProductsCategory")
* @ORM\JoinColumn(nullable=false)
*/
private $category;
public function getId(): ?int
{
return $this->id;
}
public function getCompany(): ?ExportCompany
{
return $this->company;
}
public function setCompany(?ExportCompany $company): self
{
$this->company = $company;
return $this;
}
public function getProduct(): ?Products
{
return $this->product;
}
public function setProduct(?Products $product): self
{
$this->product = $product;
return $this;
}
public function getIsActive(): ?bool
{
return $this->isActive;
}
public function setIsActive(bool $isActive): self
{
$this->isActive = $isActive;
return $this;
}
public function getCategory(): ?ProductsCategory
{
return $this->category;
}
public function setCategory(?ProductsCategory $category): self
{
$this->category = $category;
return $this;
}
}
сущность ProductsCategory :
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\ProductsCategoryRepository")
*/
class ProductsCategory
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=50)
*/
private $categoryTitle;
/**
* @ORM\Column(type="text", nullable=true)
*/
private $categoryDescription;
/**
* @ORM\Column(type="boolean")
*/
private $isActive;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Products", mappedBy="category", cascade={"persist", "remove"})
*/
private $products;
public function __construct()
{
$this->product = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getCategoryTitle(): ?string
{
return $this->categoryTitle;
}
public function setCategoryTitle(string $categoryTitle): self
{
$this->categoryTitle = $categoryTitle;
return $this;
}
public function getCategoryDescription(): ?string
{
return $this->categoryDescription;
}
public function setCategoryDescription(?string $categoryDescription): self
{
$this->categoryDescription = $categoryDescription;
return $this;
}
public function getIsActive(): ?bool
{
return $this->isActive;
}
public function setIsActive(bool $isActive): self
{
$this->isActive = $isActive;
return $this;
}
public function getProducts(): ?Products
{
return $this->products;
}
public function setProducts(Products $products): self
{
$this->products = $products;
// set the owning side of the relation if necessary
if ($products->getCategory() !== $this) {
$products->setCategory($this);
}
return $this;
}
public function __toString()
{
return $this->categoryTitle;
}
}
Сущность продуктов:
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\ProductsRepository")
*/
class Products
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=50)
*/
private $productTitle;
/**
* @ORM\Column(type="text")
*/
private $productDescription;
/**
* @ORM\Column(type="boolean")
*/
private $isActive;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\ProductsCategory", inversedBy="products", cascade={"persist", "remove"})
* @ORM\JoinColumn(nullable=false)
*/
private $category;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Exportables", mappedBy="product")
*/
private $exportables;
public function __construct()
{
$this->exportables = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getProductTitle(): ?string
{
return $this->productTitle;
}
public function setProductTitle(string $productTitle): self
{
$this->productTitle = $productTitle;
return $this;
}
public function getProductDescription(): ?string
{
return $this->productDescription;
}
public function setProductDescription(string $productDescription): self
{
$this->productDescription = $productDescription;
return $this;
}
public function getIsActive(): ?bool
{
return $this->isActive;
}
public function setIsActive(bool $isActive): self
{
$this->isActive = $isActive;
return $this;
}
public function getCategory(): ?ProductsCategory
{
return $this->category;
}
public function setCategory(ProductsCategory $category): self
{
$this->category = $category;
return $this;
}
/**
* @return Collection|Exportables[]
*/
public function getExportables(): Collection
{
return $this->exportables;
}
public function addExportable(Exportables $exportable): self
{
if (!$this->exportables->contains($exportable)) {
$this->exportables[] = $exportable;
$exportable->setProduct($this);
}
return $this;
}
public function removeExportable(Exportables $exportable): self
{
if ($this->exportables->contains($exportable)) {
$this->exportables->removeElement($exportable);
// set the owning side to null (unless already changed)
if ($exportable->getProduct() === $this) {
$exportable->setProduct(null);
}
}
return $this;
}
public function __toString(){
return $this->productTitle;
}
}
Тип экспорта:
namespace App\Form;
use App\Entity\Products;
use App\Entity\Exportables;
use App\Entity\ProductsCategory;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class ExportablesType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('company')
->add('category', EntityType::class, array(
'class' => ProductsCategory::class,
'placeholder' => 'Select a Category...',
))
->add('isActive')
;
$formModifier = function (FormInterface $form, ProductsCategory $cat = null) {
$products = null === $cat ? [] : $cat->getProducts();
dump($products);
$form->add('product', EntityType::class, [
'class' => 'App\Entity\Products',
'placeholder' => '',
'choices' => $products,
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
// this would be your entity, i.e. SportMeetup
$data = $event->getData();
$formModifier($event->getForm(), $data->getCategory());
}
);
$builder->get('category')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
// It's important here to fetch $event->getForm()->getData(), as
// $event->getData() will get you the client data (that is, the ID)
$cat = $event->getForm()->getData();
// since we've added the listener to the child, we'll have to pass on
// the parent to the callback functions!
$formModifier($event->getForm()->getParent(), $cat);
}
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Exportables::class,
]);
}
}
Экспортер Контроллер:
namespace App\Controller;
use App\Entity\Exportables;
use App\Form\ExportablesType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class ExportablesController extends AbstractController
{
/**
* @Route("/admin-panel/exportables/new", name="exportables_create")
* @Route("/admin-panel/exportables/{id}/edit", name="exportables_edit")
*/
public function exportables_create_and_edit(Request $request, EntityManagerInterface $em, Exportables $exportables = null)
{
if(!$exportables){
$exportables = new Exportables();
}
$form = $this->createForm(ExportablesType::class, $exportables);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$em->persist($exportables);
$em->flush();
}
return $this->render('/admin-panel/exportables_create.html.twig', [
'exForm' => $form->createView(),
'editMode' => $exportables->getId() !== null
]);
}
}
Наконец, файл ветки рендеринг формы:
{% extends '/admin-panel/base-admin.html.twig' %}
{% block body %}
{{ form_start(exForm) }}
<div class="form-group">
{{ form_row(exForm.company, {'attr':{'class':"form-control"}}) }}
</div>
<div class="form-group">
{{ form_row(exForm.category, {'attr':{'class':"form-control"}}) }}
</div>
{% if exForm.product is defined %}
<div class="form-group">
{{ form_row(exForm.product, {'label': "Product..", 'attr':{'class':"form-control"}}) }}
</div>
{% endif %}
<div class="form-group">
<div class="custom-control custom-checkbox">
{{ form_widget(exForm.isActive, {'attr': {'class': "custom-control-input", 'checked': "checked"}}) }}
<label class="custom-control-label" for="exportables_isActive">Visible on the website?</label>
</div>
</div>
<button type="submit" class="btn btn-success">Create</button>
{{ form_end(exForm) }}
{% endblock %}
{% block javascripts %}
<script>
{# //for some reasons this line doesnt work $(document).ready(function() { #}
jQuery(document).ready(function($){
var $cat = $('#exportables_category');
// When cat gets selected ...
$cat.change(function() {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected cat value.
var data = {};
data[$cat.attr('name')] = $cat.val();
console.log("cat val " + $cat.val());
//console.log($form.attr('method'));
const url = "{{ path('exportables_create')|escape('js') }}";
//console.log(data);
//console.log(url);
//why undefined?? console.log($form.attr('action'));
// Submit data via AJAX to the form's action path.
$.ajax({
//url : $form.attr('action'),
url : url,
type: $form.attr('method'),
data : data,
success: function(html) {
// Replace current position field ...
$('#exportables_product').replaceWith(
// ... with the returned one from the AJAX response.
//$(html).find('#exportables_product')
array(1,2,3)
);
// Position field now displays the appropriate positions.
}
});
});
});
</script>
{% endblock %}
Любая помощь с благодарностью.