Dependent dropdown list doesn't work - symfony - php

I want to generate dependent dropdown list in Symfony 3.4
I have followed the exact example(copy and paste) : https://ourcodeworld.com/articles/read/652/how-to-create-a-dependent-select-dependent-dropdown-in-symfony-3
I have created all the tables and populated them.
I really didn't change any thing I want to test the code as it is before using it in my project.
the result :
Update:
Person entity:
/**
* Person
*
* #ORM\Table(name="person")
* #ORM\Entity(repositoryClass="AppBundle\Repository\PersonRepository")
*/
class Person
{
/**
* #var integer
*
* #ORM\Column(name="id", type="bigint")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="last_name", type="string", length=255, nullable=false)
*/
private $lastName;
/**
* #var \AppBundle\Entity\City
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\City")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="city_id", referencedColumnName="id")
* })
*/
private $city;
/**
* #var \AppBundle\Entity\Neighborhood
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Neighborhood")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="neighborhood_id", referencedColumnName="id")
* })
*/
private $neighborhood;
Person type
class PersonType extends AbstractType
{
private $em;
/**
* #param EntityManagerInterface $em
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name')
->add('lastName');
$builder->addEventListener(FormEvents::PRE_SET_DATA, array($this, 'onPreSetData'));
$builder->addEventListener(FormEvents::PRE_SUBMIT, array($this, 'onPreSubmit'));
}
protected function addElements(FormInterface $form, City $city = null) {
// 4. Add the province element
$form->add('city', EntityType::class, array(
'required' => true,
'data' => $city,
'placeholder' => 'Select a City...',
'class' => 'AppBundle:City'
));
$neighborhoods = array();
if ($city) {
$repoNeighborhood = $this->em->getRepository('AppBundle:Neighborhood');
$neighborhoods = $repoNeighborhood->createQueryBuilder("q")
->where("q.city = :cityid")
->setParameter("cityid", $city->getId())
->getQuery()
->getResult();
}
$form->add('neighborhood', EntityType::class, array(
'required' => true,
'placeholder' => 'Select a City first ...',
'class' => 'AppBundle:Neighborhood',
'choices' => $neighborhoods
));
}
function onPreSubmit(FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
$city = $this->em->getRepository('AppBundle:City')->find($data['city']);
$this->addElements($form, $city);
}
function onPreSetData(FormEvent $event) {
$person = $event->getData();
$form = $event->getForm();
$city = $person->getCity() ? $person->getCity() : null;
$this->addElements($form, $city);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Person'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_person';
}
}
views/Person.html.twig:
{% extends 'base.html.twig' %}
{% block body %}
<h1>Person creation</h1>
{{ form_start(form) }}
{{ form_widget(form) }}
<input type="submit" value="Create" />
{{ form_end(form) }}
<ul>
<li>
Back to the list
</li>
</ul>
{% endblock %}
{% block javascripts %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
$('#appbundle_person_city').change(function () {
var citySelector = $(this);
$.ajax({
url: "{{ path('person_list_neighborhoods') }}",
type: "GET",
dataType: "JSON",
data: {
cityid: citySelector.val()
},
success: function (neighborhoods) {
var neighborhoodSelect = $("#appbundle_person_neighborhood");
neighborhoodSelect.html('');
neighborhoodSelect.append('<option value> Select a neighborhood of ' + citySelector.find("option:selected").text() + ' ...</option>');
$.each(neighborhoods, function (key, neighborhood) {
neighborhoodSelect.append('<option value="' + neighborhood.id + '">' + neighborhood.name + '</option>');
});
},
error: function (err) {
alert("An error ocurred while loading data ...");
}
});
});
</script>
{% endblock %}
Person controller:
class PersonController extends Controller
{
/**
* Returns a JSON string with the neighborhoods of the City with the providen id.
*
* #param Request $request
* #return JsonResponse
*/
public function listNeighborhoodsOfCityAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$neighborhoodsRepository = $em->getRepository("AppBundle:Neighborhood");
$neighborhoods = $neighborhoodsRepository->createQueryBuilder("q")
->where("q.city = :cityid")
->setParameter("cityid", $request->query->get("cityid"))
->getQuery()
->getResult();
$responseArray = array();
foreach($neighborhoods as $neighborhood){
$responseArray[] = array(
"id" => $neighborhood->getId(),
"name" => $neighborhood->getName()
);
}
// Return array with structure of the neighborhoods of the providen city id
return new JsonResponse($responseArray);
}
}
AppBundle/config/routing.yml
person_list_neighborhoods:
path: /get-neighborhoods-from-city
defaults: { _controller: "AppBundle:Person:listNeighborhoodsOfCity" }
methods: GET

Related

Symfony Error: Neither the property nor one of the methods exist and have public access in class [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 3 years ago.
Improve this question
I use symfony 3.4.
I create create,delete,edit,show pages for my Project table ..but i get this error when try show one project
Neither the property "fee" nor one of the methods "fee()", "getfee()"/"isfee()" or "__call()" exist and have public access in class "AppBundle\Entity\Project".
My entity class(/Entity/Project.php)
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="project")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ProjectRepository")
*/
class Project
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Payment", mappedBy="id")
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Conversation", mappedBy="id")
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Attachment", mappedBy="id")
*/
private $id;
/**
* #ORM\Column(type="string", length=100)
* #Assert\NotBlank(message="please fill title")
* #Assert\Length(min=3,minMessage="message from annotation: field must be at least 3")
*/
private $title;
/**
* #ORM\Column(type="string", length=100)
* #Assert\NotBlank(message="please fill type")
*/
private $type;
/**
* #ORM\Column(type="integer")
* #Assert\NotBlank(message="please fill budget")
*/
private $budget;
/**
* #ORM\Column(type="text")
*/
private $description;
/**
* #ORM\Column(type="datetime")
* #Assert\DateTime
*/
private $registerDate;
/**
* #ORM\Column(type="datetime")
* #Assert\DateTime
*/
private $endDate;
/**
* #ORM\Column(type="integer")
*/
private $prePay;
/**
* #ORM\Column(type="integer")
*/
private $fee;
/**
* #ORM\Column(type="integer", length=1)
*/
private $isPay;
/**
* #ORM\Column(type="integer", length=1)
*/
private $isPrePay;
/**
* #ORM\Column(name="attachmentId", type="string")
*
* #Assert\NotBlank(message="Please, upload the product brochure as a PDF file.")
* #Assert\File(mimeTypes={ "application/zip" })
*/
private $attachmentId;
/**
* #ORM\Column(type="smallint")
*/
private $status;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $user;
public function __construct()
{
$this->registerDate = new \DateTime();
$this->prePay = 0;
$this->fee = 0;
$this->isPay = 0;
$this->isPrePay = 0;
$this->status = 0; //#todo make this better
}
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
public function getTitle()
{
return $this->title;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getType()
{
return $this->type;
}
public function setType($type)
{
$this->type = $type;
}
public function getBudget()
{
return $this->budget;
}
public function setBudget($budget)
{
$this->budget = $budget;
}
public function getDescription()
{
return $this->description;
}
public function setDescription($description)
{
$this->description = $description;
}
public function getRegisterDate()
{
return $this->registerDate;
}
public function setRegisterDate($register_date)
{
$this->registerDate = $register_date;
}
public function getEndDate()
{
return $this->endDate;
}
public function setEndDate($end_date)
{
$this->endDate = $end_date;
}
public function getPrePay()
{
return $this->prePay;
}
public function setPrePay($pre_pay)
{
$this->prePay = $pre_pay;
}
public function geFee()
{
return $this->fee;
}
public function setFee($fee)
{
$this->fee = $fee;
}
public function getIsPay()
{
return $this->isPay;
}
public function setIsPay($is_pay)
{
$this->isPay = $is_pay;
}
public function setIsPrePay($is_prepay)
{
$this->isPrePay = $is_prepay;
}
public function getIsPrePay()
{
return $this->isPrePay;
}
public function getAttachment()
{
return $this->attachmentId;
}
public function setAttachment($attachment)
{
$this->attachmentId = $attachment;
//return $this;
}
public function getStatus()
{
return $this->status;
}
public function setStatus($status)
{
$this->status = $status;
}
public function setUserId($user_id)
{
$this->user = $user_id;
}
public function getUserId($user_id)
{
return $this->user;
}
}
My ProjectType (Form/ProjectType.php)
<?php
namespace AppBundle\Form;
use AppBundle\Entity\Project;
use AppBundle\Entity\User;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\BaseType;
use Symfony\Component\Form\Extension\Core\Type\BirthdayType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\TelType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
class ProjectType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title')
->add('type')
->add('budget',IntegerType::class)
->add('description',TextareaType::class)
->add('registerDate',DateTimeType::class)
->add('endDate',DateTimeType::class)
->add('prePay',IntegerType::class)
->add('fee',IntegerType::class,array('mapped'=>false))
->add('isPay',ChoiceType::class,[
'choices' => [
'Yes' => 1,
'No' => 0,
],
])
->add('isPrePay',ChoiceType::class,[
'choices' => [
'Yes' => 1,
'No' => 0,
],
])
->add('attachmentId',NumberType::class,array('mapped'=>false))
->add('status',ChoiceType::class,[
'choices' => [
'Pending Review' => 0,
'Pending PrePaid' => 1,
'In Process' => 2,
'End Work' => 3,
'Complete' => 4
],
])
->add('user',NumberType::class,['mapped'=>false]);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Project::class
));
}
/**
* {#inheritdoc}
*/
/*public function getBlockPrefix()
{
return 'appbundle_project';
}*/
}
My Project Controller file ( src/AppBundle/Controller/ProjectCntroller.php )
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien#symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace AppBundle\Controller\Admin;
use AppBundle\Entity\Project;
use AppBundle\Form\ProjectType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
/**
* Project controller.
*
* #Route("/admin", name="admin_project_controller")
* #Security("has_role('ROLE_ADMIN')")
*/
class ProjectController extends Controller
{
/**
* Lists all project entities.
*
* #Route("/project", name="admin_project_index")
* #Method("GET")
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$projects = $em->getRepository(Project::class)->findAll();
return $this->render('admin/project/index.html.twig', array(
'projects' => $projects,
));
}
/**
* Creates a new project entity.
*
* #Route("/project/new", name="admin_project_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$project = new Project();
$form = $this->createForm(ProjectType::class, $project);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($project);
$em->flush();
return $this->redirectToRoute('admin_project_show', array('id' => $project->getId()));
}
return $this->render('admin/project/new.html.twig', array(
'project' => $project,
'form' => $form->createView(),
));
}
/**
* Finds and displays a project entity.
*
* #Route("/project/{id}", name="admin_project_show")
* #Method("GET")
*/
public function showAction(Project $project)
{
$deleteForm = $this->createDeleteForm($project);
return $this->render('admin/project/show.html.twig', array(
'project' => $project,
'delete_form' => $deleteForm->createView(),
));
}
/**
* Displays a form to edit an existing project entity.
*
* #Route("/project/{id}/edit", name="admin_project_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Project $project)
{
$deleteForm = $this->createDeleteForm($project);
$editForm = $this->createForm(ProjectType::class, $project);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('admin_project_edit', array('id' => $project->getId()));
}
return $this->render('admin/project/edit.html.twig', array(
'project' => $project,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Deletes a project entity.
*
* #Route("/project/{id}", name="admin_project_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, Project $project)
{
$form = $this->createDeleteForm($project);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($project);
$em->flush();
}
return $this->redirectToRoute('admin_project_index');
}
/**
* Creates a form to delete a project entity.
*
* #param Project $project The project entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm(Project $project)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('admin_project_delete', array('id' => $project->getId())))
->setMethod('DELETE')
->getForm()
;
}
}
My index.twig file ( apop/Resource/views/admins/project/index.html.twig)
{% extends 'base.html.twig' %}
{% block body %}
<h1>Projects list</h1>
<table>
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>Type</th>
<th>Budget</th>
<th>Description</th>
<th>Registerdate</th>
<th>Enddate</th>
<th>Prepay</th>
<th>Fee</th>
<th>Ispay</th>
<th>Isprepay</th>
<th>Attachmentid</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for project in projects %}
<tr>
<td>{{ project.id }}</td>
<td>{{ project.title }}</td>
<td>{{ project.type }}</td>
<td>{{ project.budget }}</td>
<td>{{ project.description }}</td>
<td>{% if project.registerDate %}{{ project.registerDate|date('Y-m-d H:i:s') }}{% endif %}</td>
<td>{% if project.endDate %}{{ project.endDate|date('Y-m-d H:i:s') }}{% endif %}</td>
<td>{{ project.prePay }}</td>
<td>{{ project.fee }}</td>
<td>{% if project.isPay %}Yes{% else %}No{% endif %}</td>
<td>{% if project.isPrePay %}Yes{% else %}No{% endif %}</td>
<td>{{ project.attachmentId }}</td>
<td>{{ project.status }}</td>
<td>
<ul>
<li>
show
</li>
<li>
edit
</li>
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<ul>
<li>
Create a new project
</li>
</ul>
{% endblock %}
if i delete {{ project.fee }} from twig template i got same error but now for attachmentId and if delete {{ project.attachmentId }} theen these is no error.
Thanks.
You have typo mistake in entity class,
public function geFee()
It should,
public function getFee()

How to get user object inside form Repository in Symfony 2.8

I am developing a web app on Symfony. I have 2 entities that is question about, "Line" and "Dosier". So, 1 dosier can have many lines.
Now I'm implementing the CRUD for the Line entity, so Line entity has a "dropdown" with all Dosiers from DataBase.
The problem is that in dropdown are all dosiers from any users, but I need in this dropdown to have just options that has user_id = currentUser_id.
So, my controller action :
/**
* #Route("/add-line", name="addLine")
*/
public function createLineAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$user = $this->getUser();
$line = new Line();
$form = $this->createForm(LineType::class, $line);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$em->persist($line);
$em->flush();
return $this->redirectToRoute('homepage');
}
return $this->render('AppBundle:default:formLines.html.twig', array(
'form' => $form->createView(),
));
}//create dossier action
My LineType (form builder)
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;
class LineType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')->add('dosier')
->add('dosier', EntityType::class, array(
'class' => 'AppBundle:Dosier',
'query_builder' => function($repo) {
return $repo->dosiersOfCurrentUser();
},
'choice_label' => 'name',
))
->add('save', SubmitType::class, array(
'label' => 'Save',
'attr'=> array('class'=>'btn btn-success submitButton')
)
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Line'
));
}
public function getBlockPrefix()
{
return 'appbundle_line';
}
}
My Line.php (entity)
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Line
*
* #ORM\Table(name="line")
* #ORM\Entity(repositoryClass="AppBundle\Repository\LineRepository")
*/
class Line
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Dosier", inversedBy="lines")
* #ORM\JoinColumn(name="dosier_id", referencedColumnName="id")
*/
private $dosier;
/**
* #ORM\OneToMany(targetEntity="Loan", mappedBy="line")
*/
private $loans;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
public function __construct()
{
$this->loans = new ArrayCollection();
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Line
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Add loan
*
* #param \AppBundle\Entity\Loan $loan
*
* #return Line
*/
public function addLoan(\AppBundle\Entity\Loan $loan)
{
$this->loans[] = $loan;
return $this;
}
/**
* Remove loan
*
* #param \AppBundle\Entity\Loan $loan
*/
public function removeLoan(\AppBundle\Entity\Loan $loan)
{
$this->loans->removeElement($loan);
}
/**
* Get loans
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getLoans()
{
return $this->loans;
}
/**
* Set dosier
*
* #param \AppBundle\Entity\Dosier $dosier
*
* #return Line
*/
public function setDosier(\AppBundle\Entity\Dosier $dosier = null)
{
$this->dosier = $dosier;
return $this;
}
/**
* Get dosier
*
* #return \AppBundle\Entity\Dosier
*/
public function getDosier()
{
return $this->dosier;
}
}
and my repository : DosierRepository.php
<?php
namespace AppBundle\Repository;
use Doctrine\ORM\EntityRepository;
class DosierRepository extends \Doctrine\ORM\EntityRepository
{
public function dosiersOfCurrentUser() {
return $this->createQueryBuilder('dosier')
->where('dosier.userId = 1 ')
->orderBy('dosier.name', 'DESC');
}
}
How can I get the current user, or at least the current user id, to make a query like ... select from Dosier where dosier.user_id = $???
In controller where you are buiding your form you need pass the user object to your form type
$tokenStorage = $this->get('security.token_storage');
$form = $this->createForm(new LineType($tokenStorage), $line);
//... other stuff
Now in your form type recieve this tokenStorage object and retrieve user object and pass to your repo function
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
//.. other use statements
class LineType extends AbstractType
{
private $user;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->user = $tokenStorage->getToken()->getUser();
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$userId = $this->user->getId();
$builder
->add('name')->add('dosier')
->add('dosier', EntityType::class, array(
'class' => 'AppBundle:Dosier',
'query_builder' => function($repo) use($userId) {
return $repo->dosiersOfCurrentUser($userId);
},
'choice_label' => 'name',
))
->add('save', SubmitType::class, array(
'label' => 'Save',
'attr'=> array('class'=>'btn btn-success submitButton')
)
);
}
}
In repo apply your filter
class DosierRepository extends \Doctrine\ORM\EntityRepository
{
public function dosiersOfCurrentUser($userId) {
return $this->createQueryBuilder('dosier')
->where('dosier.userId = :userId ')
->setParameter('userId',$userId)
->orderBy('dosier.name', 'DESC');
}
}

Symfony dynamical forms

Hello I'm trying to make dynamical form and I have some problems.. I have been trying to follow http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html and some related tutorials but without result.
Currently I'm getting following error:
Catchable Fatal Error: Argument 2 passed to
UserBundle\Form\Type\CompanyRegistrationFormType::UserBundle\Form\Type{closure}()
must be an instance of UserBundle\Entity\Sector, instance of
UserBundle\Entity\Company given, called in
UserBundle/Form/Type/CompanyRegistrationFormType.php on line 82 and defined
I have 3 entity table:
Company.php
/**
* #ORM\OneToMany(targetEntity="Sector", mappedBy="company")
*/
protected $sector;
Sector.php
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="name", type="string")
*/
protected $name;
/**
* #ORM\ManyToOne(targetEntity="Company", inversedBy="sector")
*/
protected $company;
/**
* #ORM\OneToMany(targetEntity="MainCategory", mappedBy="sector")
*/
protected $mainCategory;
MainCategory.php
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $name;
/**
* #ORM\ManyToOne(targetEntity="Sector", inversedBy="mainCategory")
*/
protected $sector;
FormType.php
$builder->add('sector', 'entity', array(
'class' => 'UserBundle:Company'));
$formModifier = function (FormInterface $form, Sector $sector = null) {
$mainCategories = null === $sector ? array() : $sector->getAvailableMainCategories();
$form->add('mainCategory', 'entity', array(
'class' => 'UserBundle:Sector',
'choices' => $mainCategories));
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
$data = $event->getData();
$formModifier($event->getForm(), $data);
});
$builder->get('sector')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$sector = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $sector);
});
and twig:
{{ form_row(form.sector) }} {# <select id="company_sector" #}
{{ form_row(form.mainCategory) }} {# select id="company_mainCategory" #}
<script>
var $sector = $('#company_sector');
$sport.change(function(){
var $form = $(this).closest('form');
var data = {};
data[$sector.attr('name')] = $sector.val();
$.ajax({
url : $form.attr('action'),
type : $form.attr('method'),
data : data,
success: function(html) {
$('#company_mainCategory').replaceWith(
$(html).find('#company_mainCategory'));
}
});
});
</script>
Line 82
$formModifier($event->getForm(), $data);
if I change it to
$formModifier($event->getForm(), $data->getSector());
I get following error:
Notice: Undefined property:
UserBundle\Entity\Company::$mainCategory
can someone help me out? Thanks for your time!
$builder->add('sector', 'entity', array(
'class' => 'UserBundle:Company'));
You have to pass in class parameter classname associated with entity Sector (i suppose UserBundle:Sector)
Documentation for symfony2 entity field type

Integrate external service with Form Type on symfony (Dynamic Form)

I have a dynamic form in symfony. currently i have a external service where the data exist this services provided a way to render a form for data but i need integrate with my bussines logic into my app.
for solve this problem I implements a annotation and a CustomType. the annotation is simple holder for the name of the table in the external service
in my app i have a entity
/**
* Person
*
* #ORM\Table("person")
* #ORM\Entity
* #EService("person_data")
*/
class Person extends AbstractEntityEService
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**/
}
/**
* AbstractEntityEService
*
*/
abstract AbstractEntityEService
{
/**
* #var integer
*
* #ORM\Column(name="uuid", type="string")
*/
protected $uuid;
/**
* Holder for the data
protected $data;
/* setters and getters... */
}
the external service provide a uuid when i store data, with this uuid i can print the fields in my view and with this uuid i can update the data.
here is my CustomType
class ExternalType extends AbstractType {
/**
* #var Reader
*/
private $reader;
/**
* #var ExternalService
*/
private $ts;
public function __construct(Reader $reader, ExternalService $ts){
$this->reader = $reader;
$this->ts = $ts;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event) use ($options) {
$annotation = $this->reader->getClassAnnotation(
new \ReflectionClass($options['class']),
'MyBundle\Lib\Annotation\EServiceAnnotation'
);
// this is only form a empty form
if (null === $event->getData()) {
// here get the annotated value from entity and get a uuid
$event->setData($this->ts->getForm($annotation->getformName()));
}
$event->getForm()->getParent()->add('data', 'collection', array(
'allow_add' => true,
'label' => false
));
});
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver
->setDefaults(array(
'required' => false,
'error_bubbling' => true
))
->setRequired(array(
'class',
));
}
/**
* #inheritdoc
*/
public function getName()
{
return 'external';
}
/**
* #inheritdoc
*/
public function getParent()
{
return 'hidden';
}
}
this is the template for the CustomType
{% block external_widget %}
{% spaceless %}
<div data-form-name="{{ form.parent.vars.name }}" data-form-uuid="{{uuid}}"></div>
{% set type = type|default('hidden') %}
{{ block('form_widget_simple') }}
{% endspaceless %}
{% endblock external_widget %}
i register my ExternalType like service for use into my main form PersonType
class AsociacionType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('uuid', 'external', array('class' => $options['data_class']));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MyBundle\Entity\Person'
));
}
/**
* #return string
*/
public function getName()
{
return 'my_bundle_person';
}
}
and at the last i use the doctrine.event_subscriber
postLoad : for fill the data property in my Entities using the uuid for call external service
prePersist: for persist the data sent by users into the external service
preUpdate: for update the data sent by user into the external service
Here the events
public function postLoad(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if ($entity instanceof AbstractEntityEService && null !== $entity->getUuid())
{
$t = $this->externalService->get($entity->getUuid());
$entity->setData((array)$t->getData());
}
}
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if ($entity instanceof AbstractEntityEService) {
$t = $this->externalService->persist($entity->getData(), $entity->getUuid());
$entity->setUuid($t->getUuid());
}
}
public function preUpdate(LifecycleEventArgs $args)
{
$this->prePersist($args);
}
The new and create works fine. but when i'm going to update the data for a unknown reason the preUpdate event never is called. I think it is because the form does not mean that the data has changed. I need to know how to tell to Symfony the form has changed

Symfony2, Doctrine2 Found entity of type Doctrine\Common\Collections\ArrayCollection on association sth#category, but expecting sth

Now I have problem with submitting post data in my form (my forms looks like:
Task: <input text>
Category: <multiple select category>
DueDate: <date>
<submit>
)
And after submitting my form, I'll get this error:
Found entity of type Doctrine\Common\Collections\ArrayCollection on association Acme\TaskBundle\Entity\Task#category, but expecting Acme\TaskBundle\Entity\Category
My sources:
Task Object Task.php
<?php
namespace Acme\TaskBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="tasks")
*/
class Task
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=200)
* #Assert\NotBlank(
* message = "Task cannot be empty"
* )
* #Assert\Length(
* min = "3",
* minMessage = "Task is too short"
* )
*/
protected $task;
/**
* #ORM\Column(type="datetime")
* #Assert\NotBlank()
* #Assert\Type("\DateTime")
*/
protected $dueDate;
/**
* #Assert\True(message = "You have to agree")
*/
protected $accepted;
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="tasks")
*/
protected $category;
/**
* Constructor
*/
public function __construct()
{
$this->category = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set task
*
* #param string $task
* #return Task
*/
public function setTask($task)
{
$this->task = $task;
return $this;
}
/**
* Get task
*
* #return string
*/
public function getTask()
{
return $this->task;
}
/**
* Set dueDate
*
* #param \DateTime $dueDate
* #return Task
*/
public function setDueDate($dueDate)
{
$this->dueDate = $dueDate;
return $this;
}
/**
* Get dueDate
*
* #return \DateTime
*/
public function getDueDate()
{
return $this->dueDate;
}
/**
* Add category
*
* #param \Acme\TaskBundle\Entity\Category $category
* #return Task
*/
public function addCategory(\Acme\TaskBundle\Entity\Category $category)
{
$this->category[] = $category;
return $this;
}
/**
* Remove category
*
* #param \Acme\TaskBundle\Entity\Category $category
*/
public function removeCategory(\Acme\TaskBundle\Entity\Category $category)
{
$this->category->removeElement($category);
}
/**
* Get category
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCategory()
{
return $this->category;
}
}
Category Object Category.php
<?php
namespace Acme\TaskBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="categories")
*/
class Category
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=200, unique=true)
* #Assert\NotNull(message="Choose a category", groups = {"adding"})
*/
protected $name;
/**
* #ORM\ManyToMany(targetEntity="Task", mappedBy="category")
*/
private $tasks;
public function __toString()
{
return strval($this->name);
}
/**
* Constructor
*/
public function __construct()
{
$this->tasks = new \Doctrine\Common\Collections\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;
}
/**
* Add tasks
*
* #param \Acme\TaskBundle\Entity\Task $tasks
* #return Category
*/
public function addTask(\Acme\TaskBundle\Entity\Task $tasks)
{
$this->tasks[] = $tasks;
return $this;
}
/**
* Remove tasks
*
* #param \Acme\TaskBundle\Entity\Task $tasks
*/
public function removeTask(\Acme\TaskBundle\Entity\Task $tasks)
{
$this->tasks->removeElement($tasks);
}
/**
* Get tasks
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getTasks()
{
return $this->tasks;
}
}
TaskType TaskType.php
<?php
namespace Acme\TaskBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Acme\TaskBundle\Form\Type\Category;
class TaskType extends AbstractType
{
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\TaskBundle\Entity\Task',
'cascade_validation' => true,
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('task', 'text', array('label' => 'Task'))
->add('dueDate', 'date', array('label' => 'Due Date'))
->add('category', new CategoryType(), array('validation_groups' => array('adding')))
//->add('accepted', 'checkbox')
->add('save', 'submit', array('label' => 'Submit'));
}
public function getName()
{
return 'task';
}
}
CategoryType CategoryType.php
<?php
namespace Acme\TaskBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CategoryType extends AbstractType
{
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => null,
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', 'entity', array(
'class' => 'AcmeTaskBundle:Category',
'query_builder' => function($repository) { return $repository->createQueryBuilder('c')->orderBy('c.id', 'ASC'); },
'property' => 'name',
'multiple' => true,
'label' => 'Categories',
));
}
public function getName()
{
return 'category';
}
}
and my Controller DefaultController.php:
public function newAction(Request $request)
{
$task = new Task();
$task->setTask('Write name here...');
$task->setDueDate(new \DateTime('tomorrow'));
$form = $this->createForm('task', $task);
$form->handleRequest($request);
if($form->isValid())
{
$this->get('session')->getFlashBag()->add(
'success',
'Task was successfuly created'
);
$em = $this->getDoctrine()->getManager();
/*
$category = $this->getDoctrine()->getManager()->getRepository('AcmeTaskBundle:Category')->findOneByName($form->get('category')->getData());
$task->setCategory($category);
*/
$em->persist($task);
try {
$em->flush();
} catch (\PDOException $e) {
// sth
}
//$nextAction = $form->get('saveAndAdd')->isClicked() ? 'task_new' : 'task_success';
//return $this->redirect($this->generateUrl($nextAction));
}
return $this->render('AcmeTaskBundle:Default:new.html.twig', array('form' => $form->createView()));
}
So, I looked at this problem at the google, but there were different kinds of problems. Any idea?
UPDATE
Full error message:
[2013-09-30 14:43:55] request.CRITICAL: Uncaught PHP Exception Doctrine\ORM\ORMException: "Found entity of type Doctrine\Common\Collections\ArrayCollection on association Acme\TaskBundle\Entity\Task#category, but expecting Acme\TaskBundle\Entity\Category" at /var/www/Symfony/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php line 762 {"exception":"[object] (Doctrine\\ORM\\ORMException: Found entity of type Doctrine\\Common\\Collections\\ArrayCollection on association Acme\\TaskBundle\\Entity\\Task#category, but expecting Acme\\TaskBundle\\Entity\\Category at /var/www/Symfony/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:762)"} []
UPDATE 2
My TWIG template new.html.twig
<html>
<head>
<title>Task create</title>
</head>
<body>
{% for flashMessage in app.session.flashbag.get('success') %}
<div style="display: block; padding: 15px; border: 1px solid green; margin: 15px; width: 450px;">
{{ flashMessage }}
</div>
{% endfor %}
{{ form_start(form, {'action': path ('task_new'), 'method': 'POST', 'attr': {'novalidate': 'novalidate' }}) }}
{{ form_errors(form) }}
<div>
{{ form_label(form.task) }}:<br>
{{ form_widget(form.task) }} {{ form_errors(form.task) }}<br>
</div>
<div>
{{ form_label(form.category) }}:<br>
{{ form_widget(form.category) }} {{ form_errors(form.category) }}
</div>
<div>
{{ form_label(form.dueDate) }}:<br>
{{ form_widget(form.dueDate) }} {{ form_errors(form.dueDate) }}<br>
</div>
{{ form_end(form) }}
</body>
</html>
You can improve the controller code by changing your setters in the entities:
In Task:
public function addCategory(Category $category)
{
if (!$this->categories->contains($category)) {
$this->categories->add($category);
$category->addTask($this); // Fix this
}
}
And in Category:
public function addTask(Task $task)
{
if (!this->tasks->contains($task)) {
$this->tasks->add($task);
$task->addCategory($this);
}
}
This will keep the elements in the ArrayCollection unique so you won't have to do that checking in your code and will also set the inverse side automatically.
So I found my solution! This code works:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('task', 'text', array('label' => 'Task'))
->add('dueDate', 'date', array('label' => 'Date', 'format' => 'ddMMMMyyyy'))
->add('category', 'entity', array('required' => true, 'multiple' => true, 'class' => 'AcmeTaskBundle:Category', 'query_builder' => function($repository) { return $repository->createQueryBuilder('c')->orderBy('c.id', 'ASC'); },))
->add('save', 'submit', array('label' => 'Send'));
}
And my controller is working in this form:
if($form->isValid())
{
$this->get('session')->getFlashBag()->add(
'success',
'Task successfuly added'
);
$em = $this->getDoctrine()->getManager();
foreach($form->get('category')->getData() as $cat)
{
$task->removeCategory($cat);
$task->addCategory($cat);
}
$em->persist($task);
try {
$em->flush();
} catch (\PDOException $e) {
// sth
}
}
I'm forced to use $task->removeCategory($cat) because I'll get error of dublication primary indexes if it will not be here.
Now I'm trying to resolve my problem with embedded and non-embedded forms -> Symfony2, validation embedded and non-embedded forms with same parameters and different results?
I believe that my questions will be helpful for beginners with Symfony2 having same problems.
You are defining a manyToMany relationship from task to category, which means you can have more than one category for each task. If you are trying to have multiple categories in a task, you can try changing this line
->add('category', new CategoryType(), array('validation_groups' => array('adding')))
for something like
->add('category', 'collection', array('type' => new CategoryType()))
check symfony's documentation on form collection and this cookbook
I am not sure, but try to add a setCategory method to your Task entity, because currently you only have the add function and thats why the form component is calling the addFunction instead of the set for the whole arraycollection.
I think problem in your form.
Your task have ManyToMany relation with categotry and stored in field category(logical it is a categories). But in the form you set '->add('category', new CategoryType())' which means that you add new type that is a array or something(data_class not defined) and this type contains another one field.
Solution is to directly define field in your form(without using standalone form type) Or extend category form type from entity type(by default type extended form type).

Categories