Select box related on another select box - php

I have an entity Travel has an attribute country and I have an entity City related to Travel.
I'd like that when I choose a country showing all cities related. In fact I don't know Ajax and I need a help
class TravelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('country', 'country', array(
'required' => true,
'label' => 'Country',
))
->add('destination', 'entity',array(
'required' => true,
'class' => 'ProjectAdminBundle:City',
'property' => 'name',
'multiple' => false,
'expanded' => false,
'empty_value' => 'Choose a city',
'label' => 'Destination',
))
//.....
}
}
and this is entity Travel:
class Travel
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="country", type="string", length=255, nullable=false)
*
* #Assert\Country
*/
protected $country;
/**
* #ORM\ManyToOne(targetEntity="Project\AdminBundle\Entity\City", inversedBy="travels")
* #ORM\JoinColumn(nullable=false)
*/
protected $destination;
//........
}
Each city has a country code, for exemple:
London -> UK
Paris -> FR
.....

A similar case like yours is discussed in details in the Symphony Cookbook:
http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms

Related

Modifying label for symfony generated form

I currently have a working form in Symfony where I have a list of companies with checkboxes next to each company name. This is so you can check off which company is assigned to each user. The checkbox currently shows the accountID but it would also be helpful to have the entity field 'name' as well. Can you build a property with two entity fields? Here is my form in my controller:
->add('companies', 'entity', array(
'label' => 'Company',
'class' => 'Default\Bundle\Entity\Customer',
'property' => 'accountId', //This puts the company id next to the check box
'multiple' => true,
'expanded' => true,
'query_builder' => function ($repository)
{
return $repository->createQueryBuilder('c')->orderBy('c.accountId', 'ASC');
},))
->add('Save', 'submit')
->getForm();
This is what I am trying to do:
->add('companies', 'entity', array(
'label' => 'Company',
'class' => 'Default\Bundle\Entity\Customer',
'property' => 'accountId' + 'name', // I want to combine my entity fields here
'multiple' => true,
'expanded' => true,
'query_builder' => function ($repository)
here is the entity just for reference
class Customer
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #Assert\NotBlank(message="Please enter a Customer ID.")
* #Assert\Length(max="32")
* #ORM\Column(type="string", length=32)
* #var string
*/
protected $accountId;
/**
* #Assert\NotBlank(message="Please enter a company name.")
* #Assert\Length(max="60")
* #ORM\Column(type="string", length=60)
* #var string
*/
protected $name;
And one last time... I want to go from this:
To this:
Create a simple getter and use that as the property, eg:
public function getNamePlusAccountId()
{
return $this->name." (".$this->accountId.")";
}
and use 'property' => 'namePlusAccountId' in your form.
If you only need to change the label but would like to keep the form field value then http://symfony.com/doc/current/reference/forms/types/entity.html#choice-label probably what are you looking for

Upload PDF file with symfony

I need a help on how to upload a pdf file in symfony. In general, the relationship between the student and the pdf card is as follows: A single student can have several pdf cards and one card for a single student. The entity sheet is as follows:
class FichePDF
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Nom", type="string", length=255)
*/
private $nom;
/**
* #ORM\Column(type="string")
*
* #Assert\NotBlank(message="Please, upload the evaluation file as a PDF file.")
* #Assert\File(mimeTypes={ "application/pdf" })
*/
private $file;
/**
* #var string
*
* #ORM\Column(name="Path", type="string", length=255)
*/
private $path;
/**
* #ORM\ManyToOne(targetEntity="Polytech\SkillsBundle\Entity\Utilisateur", inversedBy="fichesPdf")
* #ORM\JoinColumn(nullable=false)
*
*/
private $etudiant;
/**
* #ORM\OneToOne(targetEntity="Polytech\SkillsBundle\Entity\SousOccasion")
* #ORM\JoinColumn(name="ssocc_id", referencedColumnName="id")
*/
private $ssocc;
With getters and setters of course. For the student entity I added this line
/**
* #ORM\OneToMany(targetEntity="Polytech\SkillsBundle\Entity\FichePDF" , mappedBy="etudiant", cascade={"remove"})
*/
private $fichesPdf;
I have a form that retrieves information about several entities in my application such as teaching units, exams and students and then retrieves a pdf file.
<?php
namespace Polytech\SkillsBundle\Form\Rapport;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class FicheOccasionType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('ues', EntityType::class,
array(
'class' => 'Polytech\SkillsBundle\Entity\UE',
'attr' => array('class' => 'browser-default ue'),
'choice_label' => 'nom',
'label' => false,
'required' => false,
'placeholder' => 'Choisissez une UE'
)
)
->add('etudiants', EntityType::class,
array(
'class' => 'Polytech\SkillsBundle\Entity\Utilisateur',
'attr' => array('class' => 'browser-default etudiants'),
'choice_label' => 'nom',
'label' => false,
'required' => false,
'placeholder' => 'Choisissez un utilisateur'
)
)
->add('file', FileType::class, array('label' => 'PDF File'))
->add('submit', HiddenType::class)
->add('export', ButtonType::class, array('label' => 'Exporter'))
->add('import', ButtonType::class, array('label' => 'Import'));
}
public function getName()
{
return 'fiche_occasion';
}
}
How can I retrieve the file as Uploaded file and add it to the database. I read the documentation and it's not exactly what I do. Can you please help me
You are halfway there as is described in the Documentation:
https://symfony.com/doc/current/controller/upload_file.html
The following steps you already did:
Adding the property to your entity
Adding the upload element to the form
Now you have to handle the uploaded file and add the upload path to the entity. In your controller where you handle the form you now have to do the following:
$fiche = new FichePDF();
$form = $this->createForm(FichePDF::class, $fiche);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$file = $fiche->getFile();
// Generate a unique name for the file before saving it
$fileName = md5(uniqid()).'.'.$file->guessExtension();
// Move the file to the directory where brochures are stored
$file->move(
$this->getParameter('upload_directory'),
$fileName
);
// Update the 'fichePDF' property to store the PDF file name
// instead of its contents
$fiche->setFile($fileName);
// Persist $fichePDF and do whatever else you want to
}

Default select option for EntityType Field

I have two classes User.php and Group.php, extended from the classes defined in fosuserbundle .
Besides this I have a ManyToMany relation between them .
The idea of the application is when I add a new user, I assign one or many groups to user (I am using material design multi select option). The problem arises when user does not have any group(s) assigned (e.g when group option is empty). I get this javascript error in the console:
An invalid form control with name='user[groups][]' is not focusable.
So to resolve this issue, I need to provide a default value for group field. How to set a default user group if group is not selected ? My classes are defined as follow.
UserType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'username',
null,
array(
'attr' => array(
'placeholder' => 'username'
),
))
->add(
'email',
null,
array(
'attr' => array(
'placeholder' => 'email'
),
))
/*->add('groups', EntityType::class, array(
'class' => 'AppBundle\Entity\Group',
'choice_label' => 'name',
'expanded' => false,
'multiple' => false
)) */
->add('groups', EntityType::class, array(
'class' => 'AppBundle\Entity\Group',
'choice_label' => 'name',
'attr'=>array(
'class' => 'mdb-select'
),
'multiple' => true,
))
->add('plainPassword', RepeatedType::class, array(
'invalid_message' => 'Les mots de passe doivent être identiques.',
'first_options' => array('label' => 'Mot de passe'),
'second_options' => array('label' => 'Répétez le mot de passe'),
))
//<input id="input-id" type="file" class="file" multiple data-show-upload="false" data-show-caption="true">
->add('enabled', null, array(
'required' => false,
))
->add('imageFile',VichFileType::class, [
'required' => false,
'download_link' => true,
'attr'=>array(
'type' => 'file',
'onchange' => 'loadFile(event)'), // not mandatory, default is true
])
;
}
GroupEntity.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('roles', CollectionType::class, array(
'entry_type' => ChoiceType::class,
'entry_options' => array(
'attr' => array(
'class'=>'mdb-select colorful-select dropdown-default'
),
'choices' => array(
'Admin' => 'ROLE_ADMIN',
'USER' => 'ROLE_USER',
),
)
))
;
}
User.php
<?php
namespace AppBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #Vich\Uploadable
* #ORM\Entity
* #ORM\Table(name="fos_user")
* #UniqueEntity(fields="usernameCanonical", errorPath="username", message="fos_user.username.already_used", groups={"Default", "Registration", "Profile"})
* #UniqueEntity(fields="emailCanonical", errorPath="email", message="fos_user.email.already_used", groups={"Default", "Registration", "Profile"})
*/
class User extends BaseUser {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*
*/
protected $id;
/**
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Group", inversedBy="users", cascade={"remove"})
* #ORM\JoinTable(name="fos_user_user_group",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="group_id", referencedColumnName="id")}
* )
*/
protected $groups;
/**
* #ORM\Column(type="integer", length=6, options={"default":0})
*/
protected $loginCount = 0;
/**
* #var \DateTime
*
* #ORM\Column(type="datetime", nullable=true)
*/
protected $firstLogin;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="user_image", fileNameProperty="imageName")
*
* #var File
*/
private $imageFile;
/**
* #ORM\Column(type="string", length=255,nullable=true)
*
* #var string
*/
private $imageName;
/**
* #ORM\Column(type="datetime",nullable=true)
*
* #var \DateTime
*/
private $updatedAt;
public function __construct() {
parent::__construct();
$this->enabled = true;
$this->groups = new ArrayCollection();
}
}
Group.php
namespace AppBundle\Entity;
use FOS\UserBundle\Model\Group as BaseGroup;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_group")
*/
class Group extends BaseGroup
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\User", mappedBy="groups")
*
*
*/
protected $users;
/**
* #var string
*/
protected $name;
/**
* #var array
*/
protected $roles;
/**
* Group constructor.
*
* #param string $name
* #param array $roles
*/
public function __construct($name, $roles = array())
{
$this->name = $name;
$this->roles = $roles;
}
}
You need to pass the default data when creating the form. For example in your controller you'll have:
$user = new User();
// Add default groups to the $user
// ...
$form = $this->createForm(UserType::class, $user);
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
// ...
}
You can achieve that by adding a 'POST_SET_DATA' or 'PRE_SUBMIT' form event to assign a group to the user if there aren't any groups in the form data.
$builder->addEventListener(FormEvents::POS_SET_DATA, function (FormEvent $event) {
$user = $event->getData();
$form = $event->getForm();
if (!$user->getGroups->count() > 0) {
//...
}
});
http://symfony.com/doc/current/form/dynamic_form_modification.html
https://symfony.com/doc/current/form/events.html
you could also use a lifecycle callback for your user entity
http://symfony.com/doc/current/doctrine/lifecycle_callbacks.html
or directly modify the object in your controller (#martin 's answer)

EntityType: Charge all the values available even thought if the option is not set

I have three entities running in Symfony:
Professional
class Professional extends User
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="TurnsProfessional", mappedBy="professional")
*/
private $turns;
}
Turn
class Turn
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="TurnsProfessional", mappedBy="turn")
*/
private $professionals;
}
TurnsProfessionals
class TurnsProfessional
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Turn", inversedBy="professionals")
* #ORM\JoinColumn(name="turn_id", referencedColumnName="id")
*/
private $turn;
/**
* #ORM\ManyToOne(targetEntity="Professional", inversedBy="turns")
* #ORM\JoinColumn(name="professional_id", referencedColumnName="id")
*/
private $professional;
/**
* #ORM\Column(type="boolean")
*/
private $status;
}
In the FormType I have this:
->add('turns', 'entity',
array('class' => 'AppBundle:TurnsProfessional',
'property' => 'label',
'multiple' => true,
'expanded' => true,
));
What I would like to do is load all the "turns" available in "Turn" entity (Monday morning, Monday evening, Tuesday morning, etc.) and show them like checkboxes in a form. If the turn is checked, the turn will be registered in TurnsProfessional with status = 1 and if not with status = 0.
When I have all the turns saved in TurnsProfessional with status = 0 or status = 1, Symfony print all the options right and everything works. But, the first time no turn are created for the professional so the add('turns') method returns an empty value with no checkboxes.
How could I show all the options available in Turn entity in this case?
Thanks!
UPDATE FormType
I've tried to add a query_builder option in the FormType:
->add('turns', EntityType::class,
array('class' => 'AppBundle:Turn',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('turn')
->orderBy('turn.id', 'ASC');
},
'choice_label' => 'label',
'multiple'=>true,
'expanded'=>true,
))
Now, the form shows all the options but when I try to save the form I get the following error:
Found entity of type AppBundle\Entity\Turn on association
AppBundle\Entity\Professional#turns, but expecting
AppBundle\Entity\TurnsProfessional
You have an error because your entity is waiting for a TurnsProfessional object and you are trying to give it a Turn object. To solve this you should not map directly the field to your entity:
->add('turns', EntityType::class,
array('class' => 'AppBundle:Turn',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('turn')
->orderBy('turn.id', 'ASC');
},
'choice_label' => 'label',
'multiple' => true,
'expanded' => true,
'mapped' => false
))
Then in your controller you can access to the result like this:
$turns = $form->get('turns);
But you have to keep in mind that $turns will only contains the Turn entities you selected in your form.

Symfony3 Forms: PropertyAccessor requires a graph of objects or arrays to operate on

I have 2 entities: Accounts and Patients:
class Patients
{
//..
/**
* #var Accounts
*
* #ORM\OneToOne(targetEntity="Accounts", mappedBy="patients")
* #Assert\Valid()
*/
private $accounts;
// ..
}
class Accounts {
// ...
/**
* #var Patients
*
* #ORM\OneToOne(targetEntity="Patients", inversedBy="accounts")
* #ORM\JoinColumn(name="patients_id", referencedColumnName="id")
*
*/
private $patients;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=100, nullable=true, unique=true)
* #Assert\NotBlank(groups={"emailValidation"})
* #Assert\Email(groups={"emailValidation"})
*
*/
private $email;
/// ...
}
I need to build a form with patients info (firstname, lastname, email) and validate it.
I did it like this:
class PatientsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// .....
->add('email', EmailType::class, array(
'mapped' => $options['formState'] == 'display' ? false : true,
'property_path' => 'accounts.email',
'data' => $options['email']
))
}
//....
$resolver->setDefaults(
array(
'formState' => 'display',
//...
}
The
'mapped' => $options['formState'] == 'display' ? false : true,
Is an ugly workaround that I had to make because, without that I had two situations:
1.
->add('email', EmailType::class, array(
'property_path' => 'accounts.email',
'data' => $options['email']
))
Will give the following error:
PropertyAccessor requires a graph of objects or arrays to operate on,
but it found type "NULL" while trying to traverse path
"accounts.email" at property "email".
->add('email', EmailType::class, array(
'mapped' => false,
'property_path' => 'accounts.email',
'data' => $options['email']
))
Is not taking into account the validation groups inside entities..
Is there an elegant way to validate the email inside the accounts entity?

Categories