I have an Entity containing Self-Referenced mapping. I would like to add new categories and subcategories to the systems but I do not know how to build the add form correctly. Gets are generated and setters are generated in Entity. I'm getting an error:
Neither the property "parent" nor one of the methods
"addParent()"/"removeParent()", "setParent()", "parent()", "__set()"
or "__call()" exist and have public access in class
"Adevo\ClassifiedsBundle\Entity\ClassifiedsCategory".
namespace XXX\ClassifiedsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="XXX\ClassifiedsBundle\Repository\ClassifiedsCategoryRepository")
* #ORM\Table(name="classifieds_categories")
*/
class ClassifiedsCategory extends ClassifiedsAbstractTaxonomy {
/**
* #ORM\OneToMany(
* targetEntity = "Classifieds",
* mappedBy = "category"
* )
*/
protected $classifieds;
/**
* #ORM\ManyToMany(targetEntity="ClassifiedsCategory", mappedBy="parent")
*/
private $children;
/**
*
* #ORM\ManyToMany(targetEntity="ClassifiedsCategory", inversedBy="children")
* #ORM\JoinTable(name="subCategory",
* joinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="parent_id", referencedColumnName="id")}
* )
*/
private $parent;
/**
* Constructor
*/
public function __construct() {
$this->children = new \Doctrine\Common\Collections\ArrayCollection();
$this->parent = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add classified
*
* #param \XXX\ClassifiedsBundle\Entity\Classifieds $classified
*
* #return ClassifiedsCategory
*/
public function addClassified(\XXX\ClassifiedsBundle\Entity\Classifieds $classified) {
$this->classifieds[] = $classified;
return $this;
}
/**
* Remove classified
*
* #param \XXX\ClassifiedsBundle\Entity\Classifieds $classified
*/
public function removeClassified(\XXX\ClassifiedsBundle\Entity\Classifieds $classified) {
$this->classifieds->removeElement($classified);
}
/**
* Get classifieds
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getClassifieds() {
return $this->classifieds;
}
/**
* Add child
*
* #param \XXX\ClassifiedsBundle\Entity\ClassifiedsCategory $child
*
* #return ClassifiedsCategory
*/
public function addChild(\XXX\ClassifiedsBundle\Entity\ClassifiedsCategory $child) {
$this->children[] = $child;
return $this;
}
/**
* Remove child
*
* #param \XXX\ClassifiedsBundle\Entity\ClassifiedsCategory $child
*/
public function removeChild(\XXX\ClassifiedsBundle\Entity\ClassifiedsCategory $child) {
$this->children->removeElement($child);
}
/**
* Get children
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getChildren() {
return $this->children;
}
/**
* Add parent
*
* #param \XXX\ClassifiedsBundle\Entity\ClassifiedsCategory $parent
*
* #return ClassifiedsCategory
*/
public function addParent(\XXX\ClassifiedsBundle\Entity\ClassifiedsCategory $parent) {
$this->parent[] = $parent;
return $this;
}
/**
* Remove parent
*
* #param \XXX\ClassifiedsBundle\Entity\ClassifiedsCategory $parent
*/
public function removeParent(\XXX\ClassifiedsBundle\Entity\ClassifiedsCategory $parent) {
$this->parent->removeElement($parent);
}
/**
* Get parent
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getParent() {
return $this->parent;
}
}
<pre>
namespace XXX\ClassifiedsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\MappedSuperclass
* #ORM\HasLifecycleCallbacks
*/
abstract class ClassifiedsAbstractTaxonomy {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=120, unique=true)
*/
private $name;
/**
* #ORM\Column(type="string", length=120, unique=true)
*/
private $slug;
protected $classifieds;
/**
* Constructor
*/
public function __construct()
{
$this->classifieds = new \Doctrine\Common\Collections\ArrayCollection();
// $this->children = new \Doctrine\Common\Collections\ArrayCollection();
// $this->parent = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add classifieds
*
* #param \XXX\ClassifiedsBundle\Entity\Classifieds $classifieds
* #return ClassifiedsCategory
*/
public function addClassifieds(\XXX\ClassifiedsBundle\Entity\Classifieds $classifieds)
{
$this->classifieds[] = $classifieds;
return $this;
}
/**
* Remove classifieds
*
* #param \XXX\ClassifiedsBundle\Entity\Classifieds $classifieds
*/
public function removeClassifieds(\XXX\ClassifiedsBundle\Entity\Classifieds $classifieds)
{
$this->classifieds->removeElement($classifieds);
}
/**
* Get classifieds
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCompanies()
{
return $this->classifieds;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return AbstractTaxonomy
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set slug
*
* #param string $slug
* #return AbstractTaxonomy
*/
public function setSlug($slug)
{
$this->slug = \XXX\ClassifiedsBundle\Libs\Utils::sluggify($slug);
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function preSave(){
if(null === $this->slug){
$this->setSlug($this->getName());
}
}
}
namespace XXX\AdminBundle\Form\Type;
use XXX\AdminBundle\Form\Type\ClassifiedsTaxonomyType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ClassifiedsCategoryType extends ClassifiedsTaxonomyType {
public function getName() {
return 'taxonomy';
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('name', 'text', array(
'label' => 'Tytuł'
))
->add('slug', 'text', array(
'label' => 'Alias'
))
->add('parent', 'entity', array(
'class' => 'XXX\ClassifiedsBundle\Entity\ClassifiedsCategory',
'property' => 'name',
'empty_value' => 'Choose a parent category',
'required' => false,
))
->add('save', 'submit', array(
'label' => 'Zapisz'
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'XXX\ClassifiedsBundle\Entity\ClassifiedsCategory'
));
}
}
Your FormType ClassifiedsCategoryType is expecting an instance of ClassifiedsCategory and this need the attribute parent so far so good. But when you handle the request from your form to the controller, the formbuilder component tries to set the entered value to the attribute via the set method on the entity. This set method (setParent) is missing in your entity class.
So you have to implement it like this:
public function setParent($parent)
{
$this->parent = $parent;
return $this;
}
Related
I have 2 entities, Exercise and Product, they have a One to One relationship (the owning side is Product), and I have an ExerciseFormType and a ProductFormType.
What I want I to be able to create an exercise, and the product associated with it at the same time.
My idea was to add the the builder of exerciseFormType, 'product' and its type would be a ProductFormType. The problem is that the product needs the exercise_id, and the exercise isn't persisted yet so it doesn't have one. Here's what I have so far (it works only for creating a product when the exercise exists).
class ExerciseType extends AbstractType {
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('name')
->add('content')
->add('language')
->add('level')
->add('skillsRequired')
->add('skillsTargetted')
->add('tags')
->add('product', ProductType::class, array(
'required' => false,
'exercise'=> $builder->getData()
)
);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver){
$resolver->setDefaults(array(
'data_class' => 'SchoolBundle\Entity\Exercise'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix(){
return 'schoolbundle_exercise';
}
class ProductType extends AbstractType {
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add('visibility')->add('price', MoneyType::class);
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$product = $event->getData();
$form = $event->getForm();
$field = (!$product || null === $product->getId()) ? 'publicationDate' : 'updateDate';
$form->add($field, DateTimeType::class, array('data' => new \DateTime()));
});
$data = array(
'data' => $options['exercise'],
'class' => Exercise::class,
'choice_label' => 'getName'
);
$builder->add('exercise', EntityType::class, $data);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver){
$resolver->setDefaults(array(
'data_class' => 'MarketBundle\Entity\Product'
));
$resolver->setRequired(array(
'exercise'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix(){
return 'marketbundle_product';
}
}
In exercise I have that
<?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;
}
}
And in Product
/**
* 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;
}
}
I've said it works for an existing exercise (and no product).
When I try to create both at the same time I get
Entities passed to the choice field must be managed. Maybe persist them in the entity manager?
It's probably from the $builder->getData(), because in that case I only passed to the form a "new Exercice()". Here's the controller action
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()));
}
(Also this is another question but when I disable a formType, it is rendered, and the value appears but it's not submitted. I would actually prefer a hiddenFormType but I need the formatting of the DateTimeFormType)
Ok, this is the solution I've made once with Symfony2.8, I hope it can helps you.
/**
* #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')
));
}
}
First, I recommend moving away from using integers as IDs. There are some issues around them, including that you can't really generate one at will in this kind of scenario. I would recommend Uuids along with ramsey/uuid, which also has a Doctrine mapping.
When you've got a situation where you need an ID before it's been persisted and the Uuid generated, generate the Uuid yourself. For instance, in a contrived example
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(),
],
]);
}
...
}
The meat of it is providing the generated Uuid in the constructor (which alternately generates it if null). Do not add a setter. Here's a more involved example:
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
)));
}
That's the ID question; the second part of your question is now easily resolved, that being giving the Uuid to the FormType instead:
$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);
...
}
}
Of course, if you need it as a field, use it with the field type of your choice instead of storing it as a variable, and then get it from that field:
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()
));
...
}
This is one of the downsides of not delegating the process of interaction to a delegate object like a command, instead of generating an entity; you're stuck constructing your object before you need it, in whole, or perverting the design to make it fit. It also makes working with assertions harder as well (you only get one set, the entity's).
I have found all subjects on google and did not get the solution.
I have this error:
Expected argument of type "PL\PlatformBundle\Entity\Module",
"Doctrine\Common\Collections\ArrayCollection" given
I want to select multiple Modules in the list but when I click on save I have the error.
My Form:
<?php
namespace PL\AppAccueilBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Doctrine\ORM\EntityRepository;
class AppAccueilModuleType extends AbstractType {
private $idCompany;
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$this->idCompany = $options['idCompany'];
$builder
->add('company', CollectionType::class, array(
'class' => 'PLUserBundle:Company',
'choice_label' => 'Name',
'multiple' => false
))
->remove('company')
->add('module', EntityType::class, array(
'class' => 'PLPlatformBundle:Module',
'choice_label' => function ($allChoices) {
if ($allChoices->getRef() != null)
return $allChoices->getRef() . ' - ' . $allChoices->getTitre() . " (" . $allChoices->getSupport()->getTitre() . ")";
else
return $allChoices->getTitre() . " (" . $allChoices->getSupport()->getTitre() . ")";
},
'multiple' => true,
'expanded' => false,
'query_builder' => function (EntityRepository $er) {
return $er->getModulesListForCompany($this->idCompany);
},
))
->add('save', SubmitType::class);;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => 'PL\AppAccueilBundle\Entity\AppAccueilModule',
'idCompany' => null
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix() {
return 'pl_appaccueilbundle_appaccueilmodule';
}
}
My Entity:
<?php
namespace PL\PlatformBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use PL\AppAccueilBundle\Entity\ParcoursModule;
use PL\AppChallengeBundle\Entity\ChallengeModule;
use PL\UserBundle\Entity\Company;
use PL\UserBundle\Entity\Logo;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Module
* #ORM\Table(name="pl_module")
* #ORM\Entity(repositoryClass="PL\PlatformBundle\Repositor\ModuleRepository")
*/
class Module {
/**
* #var int
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
* #ORM\Column(name="titre", type="string", length=255)
*/
private $titre;
/**
* #var string
* #ORM\Column(name="ref", type="string", length=255, nullable=true)
*/
private $ref;
/**
* #ORM\ManyToOne(targetEntity="PL\PlatformBundle\Entity\Support")
* #ORM\JoinColumn(nullable=false)
*/
private $support;
/**
* #ORM\ManyToOne(targetEntity="PL\PlatformBundle\Entity\Theme")
* #ORM\JoinColumn(nullable=false)
*/
private $theme;
/**
* #ORM\ManyToOne(targetEntity="PL\UserBundle\Entity\Company")
* #ORM\JoinColumn(nullable=false)
*/
private $company;
/**
* #ORM\OneToOne(targetEntity="PL\UserBundle\Entity\Logo", cascade="persist")
* #ORM\JoinTable(name="pl_logo")
* #Assert\Valid()
*/
private $logo;
/**
* #ORM\ManyToMany(targetEntity="PL\PlatformBundle\Entity\Document", cascade="persist")
* #ORM\JoinTable(name="pl_module_document")
*/
private $documents;
/**
* #ORM\ManyToMany(targetEntity="PL\PlatformBundle\Entity\ChasseZone", cascade="persist")
* #ORM\JoinTable(name="pl_module_chasse_zone")
*/
private $chasseZone;
/**
* #ORM\ManyToMany(targetEntity="PL\PlatformBundle\Entity\Question", cascade="persist")
* #ORM\JoinTable(name="pl_module_quiz")
* #ORM\OrderBy({"position" = "ASC"})
*/
private $question;
/**
* #var string
* #ORM\Column(name="code_iframe", type="string", length=4095, nullable=true)
*/
private $codeIframe;
/**
* #ORM\OneToOne(targetEntity="PL\PlatformBundle\Entity\ChasseImage", cascade="persist")
* #ORM\JoinTable(name="pl_module_chasse_image")
* #Assert\Valid()
*/
private $chasseImage;
/**
* #ORM\OneToMany(targetEntity="PL\AppAccueilBundle\Entity\ParcoursModule", mappedBy="module", cascade={"persist", "remove"}, orphanRemoval=TRUE)
*/
private $parcours;
/**
* #ORM\OneToMany(targetEntity="PL\AppChallengeBundle\Entity\ChallengeModule", mappedBy="module", cascade={"persist", "remove"}, orphanRemoval=TRUE)
*/
private $challenges;
/**
* Get id
* #return int
*/
public function getId() {
return $this->id;
}
/**
* Set titre
* #param string $titre
* #return module
*/
public function setTitre($titre) {
$this->titre = $titre;
return $this;
}
/**
* Get titre
* #return string
*/
public function getTitre() {
return $this->titre;
}
/**
* Set ref
* #param string $ref
* #return module
*/
public function setRef($ref) {
$this->ref = $ref;
return $this;
}
/**
* Get ref
* #return string
*/
public function getRef() {
return $this->ref;
}
/**
* Set support
* #param Support $support
* #return Module
*/
public function setSupport(Support $support) {
$this->support = $support;
return $this;
}
/**
* Get support
* #return Support
*/
public function getSupport() {
return $this->support;
}
/**
* Set theme
* #param Theme $theme
* #return Module
*/
public function setTheme(Theme $theme) {
$this->theme = $theme;
return $this;
}
/**
* Get theme
* #return Theme
*/
public function getTheme() {
return $this->theme;
}
/**
* Set company
*
* #param Company $company
* #return Module
*/
public function setCompany(Company $company) {
$this->company = $company;
return $this;
}
/**
* Get company
* #return Company
*/
public function getCompany() {
return $this->company;
}
/**
* Set logo
* #param Logo $logo
* #return Module
*/
public function setLogo(Logo $logo = null) {
$this->logo = $logo;
return $this;
}
/**
* Get logo
* #return Logo
*/
public function getLogo() {
return $this->logo;
}
/**
* Constructor
*/
public function __construct() {
$this->documents = new ArrayCollection();
$this->chasseZone = new ArrayCollection();
$this->question = new ArrayCollection();
$this->parcours = new ArrayCollection();
$this->challenges = new ArrayCollection();
}
/**
* Add document
* #param Document $document
* #return Module
*/
public function addDocument(Document $document) {
$this->documents[] = $document;
return $this;
}
/**
* Remove document
* #param Document $document
*/
public function removeDocument(Document $document) {
$this->documents->removeElement($document);
}
/**
* Get documents
* #return \Doctrine\Common\Collections\Collection
*/
public function getDocuments() {
return $this->documents;
}
/**
* Set codeIframe
* #param string $codeIframe
* #return Module
*/
public function setCodeIframe($codeIframe) {
$this->codeIframe = $codeIframe;
return $this;
}
/**
* Get codeIframe
* #return string
*/
public function getCodeIframe() {
return $this->codeIframe;
}
/**
* Set chasseImage
* #param ChasseImage $chasseImage
* #return Module
*/
public function setChasseImage(ChasseImage $chasseImage = null) {
$this->chasseImage = $chasseImage;
return $this;
}
/**
* Get chasseImage
* #return ChasseImage
*/
public function getChasseImage() {
return $this->chasseImage;
}
/**
* Remove chasseZone
* #param ChasseZone $ch
*/
public function removeChasseZone(ChasseZone $ch) {
$this->chasseZone->removeElement($ch);
}
/**
* Get chasseZone
* #return \Doctrine\Common\Collections\Collection
*/
public function getChasseZone() {
return $this->chasseZone;
}
/**
* Add chasseZone
* #param \PL\PlatformBundle\Entity\ChasseZone $chasseZone
* #return Module
*/
public function addChasseZone(ChasseZone $chasseZone) {
$this->chasseZone[] = $chasseZone;
return $this;
}
/**
* Get questions
* #return \Doctrine\Common\Collections\Collection
*/
public function getQuestions() {
return $this->question;
}
/**
* Add question
* #param \PL\PlatformBundle\Entity\Question $question
* #return Module
*/
public function addQuestion(Question $question) {
$this->question[] = $question;
return $this;
}
/**
* Remove question
* #param Question $question
*/
public function removeQuestion(Question $question) {
$this->question->removeElement($question);
}
/**
* Get parcours
* #return \Doctrine\Common\Collections\Collection
*/
public function getParcours() {
return $this->parcours;
}
/**
* Add parcours
* #param ParcoursModule $parcours
* #return Module
*/
public function addParcours(ParcoursModule $parcours) {
$this->parcours[] = $parcours;
$parcours->setModule($this);
return $this;
}
/**
* Remove parcours
* #param ParcoursModule $parcours
*/
public function removeParcours(ParcoursModule $parcours) {
$this->parcours->removeElement($parcours);
}
/**
* Get Challenges
* #return ArrayCollection
*/
public function getChallenges() {
return $this->challenges;
}
/**
* Add challenge module
* #param ChallengeModule $challenge
* #return Module
*/
public function addChallenge(ChallengeModule $challenge) {
$this->challenges[] = $challenge;
$challenge->setModule($this);
return $this;
}
/**
* Remove challenges module
* #param ChallengeModule $challenge
*/
public function removeChallenges(ChallengeModule $challenge) {
$this->challenges->removeElement($challenge);
}
}
Thinks
I'm working my way through Symfony trying to learn how it all fits together and I'm working on the admin section.
Right now I'm putting together an admin form for a Show Entity which will reference a section entity (so this show belongs in that section, etc). Every other field in the form saves EXCEPT for the related entity choice field.
This is the ShowAdmin class
<?php
namespace AppBundle\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Nelmio\ApiDocBundle\Tests\Fixtures\Form\EntityType;
class ShowAdmin extends AbstractAdmin {
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('title', 'text')
->add('shortname', 'text')
->add('section_id', EntityType::class, array(
'class' => 'AppBundle:SectionEntity',
'choice_label' => 'section_title',
))
->add('logo', 'text')
->add('description', 'textarea')
->add('status', 'integer');
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper->add('title');
$datagridMapper->add('shortname');
}
protected function configureListFields(ListMapper $listMapper)
{
$listMapper->addIdentifier('title');
$listMapper->add('shortname', 'text');
}
}
This is the ShowEntity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="shows")
*/
class ShowEntity {
function __construct() {
$this->children = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=100)
*/
private $title;
/**
* #ORM\Column(type="string", length=100)
*/
private $shortname;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\SectionEntity")
*/
private $section;
/**
* #ORM\Column(type="string", length=255)
*/
private $logo;
/**
* #ORM\Column(type="text")
*/
private $description;
/**
* #ORM\Column(type="integer")
*/
private $status;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return ShowEntity
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set sectionId
*
* #param integer $sectionId
*
* #return ShowEntity
*/
public function setSectionId($sectionId)
{
$this->section_id = $sectionId;
return $this;
}
/**
* Get sectionId
*
* #return integer
*/
public function getSectionId()
{
return $this->section_id;
}
/**
* Set logo
*
* #param string $logo
*
* #return ShowEntity
*/
public function setLogo($logo)
{
$this->logo = $logo;
return $this;
}
/**
* Get logo
*
* #return string
*/
public function getLogo()
{
return $this->logo;
}
/**
* Set description
*
* #param string $description
*
* #return ShowEntity
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set status
*
* #param integer $status
*
* #return ShowEntity
*/
public function setStatus($status)
{
$this->status = $status;
return $this;
}
/**
* Get status
*
* #return integer
*/
public function getStatus()
{
return $this->status;
}
/**
* Set shortname
*
* #param string $shortname
*
* #return ShowEntity
*/
public function setShortname($shortname)
{
$this->shortname = $shortname;
return $this;
}
/**
* Get shortname
*
* #return string
*/
public function getShortname()
{
return $this->shortname;
}
}
And this is the SectionEntity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* SectionEntity
*
* #ORM\Table(name="section_entity")
* #ORM\Entity(repositoryClass="AppBundle\Repository\SectionEntityRepository")
*/
class SectionEntity
{
protected $section_id;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="text")
*/
private $section_title;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set sectionTitle
*
* #param string $sectionTitle
*
* #return SectionEntity
*/
public function setSectionTitle($sectionTitle)
{
$this->section_title = $sectionTitle;
return $this;
}
/**
* Get sectionTitle
*
* #return string
*/
public function getSectionTitle()
{
return $this->section_title;
}
/**
* Get string
*/
public function __toString() {
return $this->section_title;
}
function __construct() {
$this->section_id = new \Doctrine\Common\Collections\ArrayCollection();
}
}
Any help would be greatly appreciated, I know it's probably something super simple that I'm just not seeing.
Thanks.
(optional) Rename ShowEntity::$section into ShowEntity::$sections to highlight sections is a collection but not a single entity.
Set ShowEntity __construct method body to:
$this->sections = new \Doctrine\Common\Collections\ArrayCollection();
At ShowAdmin::configureFormFields rename
->add('section_id', EntityType::class, array(
into
->add('section', EntityType::class, array(
You should use direct reference to the relation instead of id.
Remove SectionEntity::__construct method, it has no sense.
Remove protected $section_id; from SectionEntity.
Change public function setSectionId($sectionId) into public function setSection(Section $section).
Perhaps you also need to rename section_title into sectionTitle or simply title, not sure about that.
Experiencing some serious problems in getting a OneToMany relationship to work. The error happens when trying to add "Children" to Beauties in Sonata Admin.
Using:
Symfony2
Doctrine
Sonata Admin
Been trying to follow the guide at: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-bidirectional
and
http://sonata-project.org/bundles/doctrine-orm-admin/2-2/doc/reference/form_field_definition.html
Error:
"CRITICAL - Uncaught PHP Exception
Doctrine\ORM\ORMInvalidArgumentException: "A new entity was found
through the relationship
'Beautify\BeautiesBundle\Entity\Beauty#children' that was not
configured to cascade persist operations for entity:
Beautify\BeautiesBundle\Entity\Children#000000000eff91d5000000017f8ca53b.
To solve this issue: Either explicitly call EntityManager#persist() on
this unknown entity or configure cascade persist this association in
the mapping for example #ManyToOne(..,cascade={"persist"}). If you
cannot find out which entity causes the problem implement
'Beautify\BeautiesBundle\Entity\Children#__toString()' to get a clue."
at
/Users/gmf/symfony/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php
line 91 "
.
.
BeautyAdmin.php
<?php
// src/Beautify/BeautiesBundle/Admin/BeautyAdmin.php
namespace Beautify\BeautiesBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Knp\Menu\ItemInterface as MenuItemInterface;
use Beautify\CategoryBundle\Entity\Category;
use Beautify\CategoryBundle\Entity\Children;
class BeautyAdmin extends Admin
{
// Fields to be shown on create/edit forms
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('General')
->add('name', 'text', array('label' => 'Name'))
->add('content', 'textarea', array('label' => 'Content'))
->add('imageFile', 'file', array('label' => 'Image file', 'required' => false))
->end()
->with('Category')
->add('category', 'sonata_type_model', array('expanded' => true))
->end()
->with('Children')
->add('children', 'sonata_type_collection', array(), array(
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position'
))
->end()
;
}
// Fields to be shown on filter forms
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('name')
->add('content')
->add('category')
;
}
// Fields to be shown on lists
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('name')
->add('content')
->add('category')
->add('imageName')
->add('_action', 'actions', array(
'actions' => array(
'show' => array(),
'edit' => array(),
'delete' => array(),
)
))
;
}
}
Beauty.php Entity
<?php
// src/Beautify/BeautiesBundle/Entity/Beauty.php
namespace Beautify\BeautiesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="beauties")
* #ORM\HasLifecycleCallbacks
* #Vich\Uploadable
*/
class Beauty
{
/**
* #ORM\OneToMany(targetEntity="Children", mappedBy="beauties")
**/
private $children;
public function __construct()
{
$this->children = new ArrayCollection();
}
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=100)
*/
protected $name;
/**
* #ORM\Column(type="text")
*/
protected $content;
/**
* #Vich\UploadableField(mapping="beauty_image", fileNameProperty="imageName")
* #var File $imageFile
*/
protected $imageFile;
/**
* #ORM\Column(type="string", length=255, nullable=true, name="image_name")
* #var string $imageName
*/
protected $imageName;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="beauties")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($image) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTime('now');
}
}
/**
* #return File
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* #param string $imageName
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
}
/**
* #return string
*/
public function getImageName()
{
return $this->imageName;
}
public function __toString()
{
return ($this->getName()) ? : '';
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Beauty
*/
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 Beauty
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set category
*
* #param \Beautify\BeautiesBundle\Entity\Category $category
* #return Beauty
*/
public function setCategory(\Beautify\BeautiesBundle\Entity\Category $category = null)
{
$this->category = $category;
return $this;
}
/**
* Get category
*
* #return \Beautify\BeautiesBundle\Entity\Category
*/
public function getCategory()
{
return $this->category;
}
/**
* Set children
*
* #param \Beautify\BeautiesBundle\Entity\Children $children
* #return Beauty
*/
public function setChildren(\Beautify\BeautiesBundle\Entity\Children $children = null)
{
$this->children = $children;
return $this;
}
/**
* Get children
*
* #return \Beautify\BeautiesBundle\Entity\Children
*/
public function getChildren()
{
return $this->children;
}
/**
* Add children
*
* #param \Beautify\BeautiesBundle\Entity\Children $children
* #return Beauty
*/
public function addChild(\Beautify\BeautiesBundle\Entity\Children $children)
{
$this->children[] = $children;
return $this;
}
/**
* Remove children
*
* #param \Beautify\BeautiesBundle\Entity\Children $children
*/
public function removeChild(\Beautify\BeautiesBundle\Entity\Children $children)
{
$this->children->removeElement($children);
}
}
Children.php Entity
<?php
namespace Beautify\BeautiesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Children
*
* #ORM\Table(name="children")
* #ORM\Entity
*/
class Children
{
/**
* #ORM\ManyToOne(targetEntity="Beauty", inversedBy="children")
* #ORM\JoinColumn(name="beauty_id", referencedColumnName="id")
**/
private $beauties;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="image", type="string", length=255)
*/
private $image;
/**
* #var string
*
* #ORM\Column(name="content", type="text")
*/
private $content;
/**
* #var integer
*
* #ORM\Column(name="priority", type="integer")
*/
private $priority;
/**
* #var integer
*
* #ORM\Column(name="parent_id", type="integer")
*/
private $parentId;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set image
*
* #param string $image
* #return Children
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* #return string
*/
public function getImage()
{
return $this->image;
}
/**
* Set content
*
* #param string $content
* #return Children
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set priority
*
* #param integer $priority
* #return Children
*/
public function setPriority($priority)
{
$this->priority = $priority;
return $this;
}
/**
* Get priority
*
* #return integer
*/
public function getPriority()
{
return $this->priority;
}
/**
* Set parentId
*
* #param integer $parentId
* #return Children
*/
public function setParentId($parentId)
{
$this->parentId = $parentId;
return $this;
}
/**
* Get parentId
*
* #return integer
*/
public function getParentId()
{
return $this->parentId;
}
/**
* Set beauties
*
* #param \Beautify\BeautiesBundle\Entity\Beauty $beauties
* #return Children
*/
public function setBeauties(\Beautify\BeautiesBundle\Entity\Beauty $beauties = null)
{
$this->beauties = $beauties;
return $this;
}
/**
* Get beauties
*
* #return \Beautify\BeautiesBundle\Entity\Beauty
*/
public function getBeauties()
{
return $this->beauties;
}
}
Very thankful for any help...
The error says it all. You need to add cascade="persist" to the children property.
/**
* #ORM\OneToMany(targetEntity="Children", mappedBy="beauties", cascade={"persist"})
**/
private $children;
See http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-associations.html#transitive-persistence-cascade-operations
I have two entities Store and Category and each Store has it's own categories.
I'd like that when a Store owner's try to add a new category and category_parent, just the categories related to current Store will be displayed.
Right now, all categories are displayed in the select-option.
I'm using Tree Gedmo extension to manage Category entity and I use getChildrenQueryBuilder method to select categories.
How can I modify this method and add my specific constraint ?
$store which is the constraint is declared in the controller action down.
I'd like te set current Category option disabled when he try to add a category_parent, so category and category parent must be differnt.
I hope it's clear
CategoryType.php
<?php
namespace Project\StoreBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
class CategoryType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
//..........
//..........
->add('category', 'entity', array(
'required' => false,
'label' => 'Category parent',
'class' => 'ProjectStoreBundle:Category',
'attr' => array('class' => 'col-sm-8'),
'empty_value' => 'Select one category',
'property' => 'indentedName',
'multiple' => false,
'expanded' => false ,
'query_builder' => function (\Project\StoreBundle\Entity\CategoryRepository $r)
{
return $r->getChildrenQueryBuilder(null, null, 'root', 'asc', false);
}
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Project\StoreBundle\Entity\Category'
));
}
/**
* #return string
*/
public function getName()
{
return 'project_storebundle_category';
}
}
Entity/Category.php
<?php
namespace Project\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Category
* #Gedmo\Tree(type="nested")
* #ORM\Table()
* #ORM\Entity(repositoryClass="Project\StoreBundle\Entity\CategoryRepository")
* #ORM\HasLifeCycleCallbacks()
*/
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=255)
*
*#Assert\NotBlank(message="Please enter the name of categorie.")
*/
private $name;
/**
* #Gedmo\slug(fields={"name"}, unique_base="uniqueBase")
* #ORM\Column(name="slug",length=255, unique=false)
*/
private $slug ;
/**
* #ORM\Column(name="uniqueBase", type="integer")
*/
private $uniqueBase ;
/**
* #ORM\Column(name="description", type="text", nullable=true)
*/
private $description;
/**
* #ORM\Column(name="metaDescription", type="string", length=255, nullable=true)
*
* #Assert\Length(
* max=255,
* maxMessage="message"
* )
*/
private $metaDescription;
/**
* #ORM\Column(name="metaKeywords", type="string", length=255, nullable=true)
*
* #Assert\Length(
* max=255,
* maxMessage="message"
* )
*/
private $metaKeywords;
/**
* #ORM\Column(name="enabled", type="boolean", nullable=false)
*/
private $enabled;
/**
* #Gedmo\TreeLeft
* #ORM\Column(name="lft", type="integer")
*/
private $lft;
/**
* #Gedmo\TreeLevel
* #ORM\Column(name="lvl", type="integer")
*/
private $lvl;
/**
* #Gedmo\TreeRight
* #ORM\Column(name="rgt", type="integer")
*/
private $rgt;
/**
* #Gedmo\TreeRoot
* #ORM\Column(name="root", type="integer", nullable=true)
*/
private $root;
/**
* #Gedmo\TreeParent
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
* #ORM\OrderBy({"lft" = "ASC"})
*/
private $children;
/**
*non mapped property
*/
private $indentedName;
/**
*non mapped property
*/
private $category;
/**
* #ORM\ManyToOne(targetEntity="Project\StoreBundle\Entity\Store", inversedBy="categories", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*/
private $store ;
/**
* Constructor
*/
public function __construct()
{
$this->children = new ArrayCollection();
}
/**
* 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 slug
*
* #param string $slug
* #return Category
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* Set uniqueBase
*
* #param integer $uniqueBase
* #return Category
*/
public function setUniqueBase($uniqueBase)
{
$this->uniqueBase = $uniqueBase;
return $this;
}
/**
* Get uniqueBase
*
* #return integer
*/
public function getUniqueBase()
{
return $this->uniqueBase;
}
/**
* Set description
*
* #param string $description
* #return Category
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set metaDescription
*
* #param string $metaDescription
* #return Category
*/
public function setMetaDescription($metaDescription)
{
$this->metaDescription = $metaDescription;
return $this;
}
/**
* Get metaDescription
*
* #return string
*/
public function getMetaDescription()
{
return $this->metaDescription;
}
/**
* Set metaKeywords
*
* #param string $metaKeywords
* #return Category
*/
public function setMetaKeywords($metaKeywords)
{
$this->metaKeywords = $metaKeywords;
return $this;
}
/**
* Get metaKeywords
*
* #return string
*/
public function getMetaKeywords()
{
return $this->metaKeywords;
}
/**
* Set enabled
*
* #param boolean $enabled
* #return Category
*/
public function setEnabled($enabled)
{
$this->enabled = $enabled;
return $this;
}
/**
* Get enabled
*
* #return boolean
*/
public function getEnabled()
{
return $this->enabled;
}
/**
* Set parent
*
* #param \Project\StoreBundle\Entity\Category $parent
* #return Category
*/
public function setParent(\Project\StoreBundle\Entity\Category $parent = null)
{
$this->parent = $parent;
return $this;
}
/**
* Get parent
*
* #return \Project\StoreBundle\Entity\Category
*/
public function getParent()
{
return $this->parent;
}
/**
* Set lft
*
* #param integer $lft
* #return Category
*/
public function setLft($lft)
{
$this->lft = $lft;
return $this;
}
/**
* Get lft
*
* #return integer
*/
public function getLft()
{
return $this->lft;
}
/**
* Set lvl
*
* #param integer $lvl
* #return Category
*/
public function setLvl($lvl)
{
$this->lvl = $lvl;
return $this;
}
/**
* Get lvl
*
* #return integer
*/
public function getLvl()
{
return $this->lvl;
}
/**
* Set rgt
*
* #param integer $rgt
* #return Category
*/
public function setRgt($rgt)
{
$this->rgt = $rgt;
return $this;
}
/**
* Get rgt
*
* #return integer
*/
public function getRgt()
{
return $this->rgt;
}
/**
* Set root
*
* #param integer $root
* #return Category
*/
public function setRoot($root)
{
$this->root = $root;
return $this;
}
/**
* Get root
*
* #return integer
*/
public function getRoot()
{
return $this->root;
}
/**
* Add children
*
* #param \Project\StoreBundle\Entity\Category $children
* #return Category
*/
public function addChild(\Project\StoreBundle\Entity\Category $children)
{
$this->children[] = $children;
return $this;
}
/**
* Remove children
*
* #param \Project\StoreBundle\Entity\Category $children
*/
public function removeChild(\Project\StoreBundle\Entity\Category $children)
{
$this->children->removeElement($children);
}
/**
* Get children
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getChildren()
{
return $this->children;
}
/**
* Get IndentedName
*
*/
public function getIndentedName()
{
return str_repeat("-----", $this->lvl).$this->name;
}
/**
* Get category
*
*/
public function getCategory()
{
return $this->category;
}
/**
* Set store
*
* #param \Project\StoreBundle\Entity\Store $store
* #return Category
*/
public function setStore(\Project\StoreBundle\Entity\Store $store = null)
{
$this->store = $store;
return $this;
}
/**
* Get store
*
* #return \Project\StoreBundle\Entity\Store
*/
public function getStore()
{
return $this->store;
}
}
Controller
/**
* Create a new Category entity.
*
*/
/**
* #ParamConverter("store", options={"mapping": {"store_id":"id"}})
*/
public function newAction(Store $store)
{
// keep in mind, this will call all registered security voters
if (false === $this->get('security.context')->isGranted('edit', $store)) {
throw new AccessDeniedException('Unauthorised access!');
}
$category = new Category();
$category->setStore($store);
$category->setUniqueBase($store->getId());
$form = $this->createForm(new CategoryType(), $category);
$request = $this->getRequest();
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
$this->get('session')->getFlashBag()->add('message', 'Category enregistred');
return $this->redirect( $this->generateUrl('dashboard_category_index', array('store_id' => $store->getId())));
}
}
return $this->render('ProjectDashboardBundle:Category:new.html.twig',
array(
'form' => $form->createView() ,
'store' =>$store,
));
}
I managed to pass the parameter $store to the form, but I don't know how to use it as a constraint in getChildrenQueryBuilder method.
Should I create a new custom method? I prefer to use getChildrenQueryBuilder if it is possible.
Here is the new code
CategoryType.php
class CategoryType extends AbstractType
{
private $store;
public function __construct($store)
{
$this->store = $store;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$store = $this->store;
$builder
//...........
//...........
->add('parent', 'entity', array(
'required' => false,
'label' => 'Category parent',
'class' => 'ProjectStoreBundle:Category',
'attr' => array('class' => 'col-sm-8'),
'empty_value' => 'Select one category',
'property' => 'indentedName',
'multiple' => false,
'expanded' => false ,
'query_builder' => function (\Project\StoreBundle\Entity\CategoryRepository $r) use ($store)
{
return $r->getChildrenQueryBuilder(null, null, 'root', 'asc', false);
}
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Project\StoreBundle\Entity\Category'
));
}
/**
* #return string
*/
public function getName()
{
return 'project_storebundle_category';
}
}
Controller
/**
* #ParamConverter("store", options={"mapping": {"store_id":"id"}})
*/
public function newAction(Store $store)
{
// keep in mind, this will call all registered security voters
if (false === $this->get('security.context')->isGranted('edit', $store)) {
throw new AccessDeniedException('Unauthorised access!');
}
$category = new Category();
$category->setStore($store);
$category->setUniqueBase($store->getId());
$form = $this->createForm(new CategoryType($store), $category);
$request = $this->getRequest();
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
$this->get('session')->getFlashBag()->add('message', 'Category enregistred');
return $this->redirect( $this->generateUrl('dashboard_category_index', array('store_id' => $store->getId())));
}
}
return $this->render('ProjectDashboardBundle:Category:new.html.twig',
array(
'form' => $form->createView() ,
'store' =>$store,
));
}
You're not saying which Symfony2 version you're using, but generically speaking, you just need to find a way to pass the current store to your form builder and use it in the method that filters the possible categories.
Take a look at this one, or this one, basically you just need to inject the $store into your form builder, and then you (almost) have it :-)