The problem was too old version of PHPunit
/////
Now I have other problem.I have that test:
class TodoTypeTest extends TypeTestCase
{
private $em;
protected function setUp()
{
$this->em = $this->createMock(EntityManager::class);
parent::setUp();
}
protected function getExtensions()
{
return array(
new PreloadedExtension([
new TodoType($this->em)
], [])
);
}
public function testTodoType()
{
$task = new Todo();
$form = $this->factory->create(TodoType::class, $task, ['locale' => 'en']);
}
}
I get this problem:
Error: Call to a member function getPrioritysInUserLocaleToForm() on null
Problem is here in TodoType class:
class TodoType extends AbstractType
{
/**
* #var EntityManagerInterface
*/
private $em;
/**
* TodoType constructor.
*
* #param EntityManagerInterface $em
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', Type\TextType::class)
->add('content', Type\TextareaType::class)
->add('priority', Type\ChoiceType::class, [ 'choices' => $this->addChoicesInUserLocale($options['locale']) ])
->add('dueDate', Type\DateTimeType::class, [
'widget' => 'single_text',
'attr' => ['class' => 'js-datepicker'],
'html5' => false,
]);
}
/**
* Configure defaults options
*
* #param OptionsResolver $resolver
*/
public function configureOptions( OptionsResolver $resolver )
{
$resolver->setDefaults( [
'locale' => 'en',
] );
}
/**
* Method adds array with choices to ChoiceType in builder
*
* #param string $locale User's locale
*
* #return array All priority in user _locale formatted as array e.g. ['1' => 'low', ...]
*/
private function addChoicesInUserLocale(string $locale): array
{
return $this->em->getRepository('AppBundle:Priority')
->getPrioritysInUserLocaleToForm($locale);
}
}
I do not know why it is not working :/
As the error message tells you, you are trying to call a method on null in this part of the code:
return $this->em->getRepository('AppBundle:Priority')
->getPrioritysInUserLocaleToForm($locale);
That message tells you, that getRepository() returns null.
This happens because the EntityManager you use is a mock, which will always return null for all methods unless specified otherwise. You can fix this by making it return an EntityRepository instead. You can do this in your test method:
public function testTodoType()
{
$repoMock = $this->createMock(PriorityRepository::class);
$repoMock->expects($this->any())
->method('getPrioritysInUserLocaleToForm')
->with('en')
->willReturn([]); // Whatever you want it to return
$this->em->expects($this->any())
->method('getRepository')
->with('AppBundle:Priority')
->willReturn($repoMock);
$task = new Todo();
$form = $this->factory->create(TodoType::class, $task, ['locale' => 'en']);
}
This will make the EntityManager return your Repository-mock which then in turn returns whatever values you want. The expects() calls are assertions and since your test does not have any yet, you might want to check that the repository method is called using $this->atLeastOnce() instead of $this->any(). In any case for the test to be useful you have to make assertions at some point.
Related
I have a problem with a nested form. I can't get the values I want to pass.
Here is a simple example to reproduce my problem, I would like to pre-fill a form about a user according to the selected house in my form.
Here are the files, if you want to test. I would like to inject the values of roger and billy the good way but my user fields are always empty
The models
class Test
{
/**
* #var string|null
*/
private $house;
/**
* #var TestUser|null
*/
private $user;
// Getters & Setters of course...
}
class TestUser
{
/**
* #var string|null
*/
private $name;
/**
* #var int|null
*/
private $age;
// Getters & Setters again...
}
The main form
class TestType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('house', ChoiceType::class, [
'choices' => [
'first' => 1,
'second' => 2,
],
]
);
$builder->get('house')->addEventListener(FormEvents::POST_SUBMIT, [$this, 'addUser']);
}
function addUser(FormEvent $event)
{
$form = $event->getForm()->getParent();
$house = $event->getForm()->getData();
if (!$house) {
return;
}
// here is the part where I choose the user I want to use
// for the rest of the example (which does not work)
$testUser = $house === 1
? (new TestUser())->setName('roger')->setAge(65)
: (new TestUser())->setName('billy')->setAge(22);
$builder = $form->getConfig()->getFormFactory()->createNamedBuilder('user', TestUserType::class, $testUser, [
'auto_initialize' => false,
]);
$form->add($builder->getForm());
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Test::class,
]);
}
}
The user form type
class TestUserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, ['label' => 'username'])
->add('age', IntegerType::class, ['label' => 'age']);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => TestUser::class,
]);
}
}
The controller
public function testForm(Request $request): RedirectResponse|Response
{
// ...
$test = new Test();
$form = $this->createForm(TestType::class, $test);
$form->handleRequest($request);
// ...
}
The view
{{ form_start(formtest) }}
<p>
<button class="btn btn-primary" type="submit">test go</button>
</p>
{{ form_end(formtest) }}
all help is welcome
Is setter actually returning $this?
When ->setAge(65) is executed.
Because it's not clear, it's not in your code you provided here.
you need to link the $user $age $house and $name to the input fields you have.
The reason why you always get an empty output is do to the face that non of the variables refer to any data source.
I'm trying to build a very simple rest api with FOS Rest bundle.
The GET and DELETE methods were easy, but I'm struggling with post.
Here's my post method of a very simple entity (only has a "name" and "active" property):
/**
* #param Request $request
* #return array|View
*/
public function postSkillsAction(Request $request)
{
$skill = new Skill();
$form = $this->createForm(SkillType::class, $skill);
$form->submit($request->request->get($form->getName()));
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($skill);
$this->entityManager->flush();
return $this->redirectToRoute('skillset', ['id' => $skill->getId()], Response::HTTP_CREATED);
}
return [
'form' => $form
];
}
And this is my form:
final class SkillType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) : void
{
$builder
->add(
'name', TextType::class, [
'label' => 'fields.name'
])
->add('active', CheckboxType::class, [
'label' => 'fields.active',
'required' => false
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Skill::class,
'csrf_protection' => false
]);
}
/**
* #return string
*/
public function getName() : string
{
return 'skill';
}
}
The problem is that it seems like the form is not filling the entity data, when I execute /api/skills sending by post name and active I get the following SQL error
An exception occurred while executing 'INSERT INTO skill (name, active, created_at, updated_at) VALUES (?, ?, ?, ?)' with params [null, 0, "2017-03-19 19:49:37", "2017-03-19 19:49:37"]
The form data arrives correctly, I've debug and if I do $request->request->get('name') I got the proper value.
I couldn't find any updated example, this one for instance is for symfony 2, although I tried to follow it as much as possible
https://github.com/gimler/symfony-rest-edition/blob/2.7/src/AppBundle/Controller/NoteController.php
UPDATE
If I do a var_dump of var_dump($request->request->all()); I get
array(
'name' => a,
'active' => 1
)
And here's the entity
final class Skill
{
use OptionalDateTimeTrait;
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $name;
/**
* #var integer
*/
private $active;
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
* #return Skill
*/
public function setId(int $id) : self
{
$this->id = $id;
return $this;
}
/**
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #param string $name
* #return self
*/
public function setName(string $name) : self
{
$this->name = $name;
return $this;
}
/**
* Set active
*
* #param boolean $active
*
* #return Skill
*/
public function setActive($active) : self
{
$this->active = $active;
return $this;
}
/**
* Get active
*
* #return boolean
*/
public function getActive()
{
return $this->active;
}
}
As you've told us, when you're calling $request->request->get('name') you get correct value. Please take a look at your code where you pass data to form:
$form->submit($request->request->get($form->getName()));
This line of code means that you're passing to form only those data, which comes as array named as your form (in your case - skill). In this case you should pass through POST such data:
skill[name]=John
skill[active]=1
If you want not to send data through POST using array wrapper, you have to submit to your form whole request:
$form->submit($request->request->all());
Both methods are technically correct, but second is in fact anti-pattern.
I'm having a problem with Symfony options resolver where I need to specify a list of defined variables that should be normalized.
The problem is: I don't want to define all these variables again in
$resolver->setDefined();
because I have a list of defined fields in $builder and the same fields are defined in the entity SlotRequest.
Is there a different way of assigning all fields/variables from entity to resolver?
First approach:
$resolver->setDefined([
'date_form','etc..' ]);
But, it pointless because in the real world I have to normalize 10+ variables + 20 fields)
Second approach would be to parse all annotations from the entity 'SlotRequest', and then fill up an array with that object.
$resolver->setDefined($anArrayOfParsedFieldsFromEntity);
Is there a better way of doing this?
An example of using:
In controller:
$form = $this->createForm(new SlotRequestType(), new SlotRequest());
SlotRequestType:
class SlotRequestType extends AbstractType
{
/**
* #var CCriteria
*/
protected $resolved = null;
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('date_from',null,['property_path'=>'dateFrom']);
//more fields
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$this->resolved = new \CCriteria();
$resolver->setDefaults(array(
'data_class' => SlotRequest::class,
'allow_extra_fields' => true,
'method' => 'GET'
));
$resolver->setDefined([....]);// the list of fields
$resolver->setNormalizer('date_from', function (Options $options, $value) {
$dateFrom = new \DateTime($value);
$this->resolved->setStartDate($dateFrom->getTimestamp());
return $value;
});
//more normalizers
}
/**
* #return null
*/
public function getName()
{
return null;
}
/**
* #return CCriteria
*/
public function getResolved()
{
return $this->resolved;
}
Entity SlotRequest
<?php
namespace Test/Entity;
class SlotRequest
{
/**
* #var string
* #Assert\NotBlank(message="Parameter [date_from] is missing.")
* #Assert\Type(
* type="string",
* message="The value {{ value }} is not a valid {{ type }}."
* )
* #Assert\Date()
*/
public $dateFrom;
//more fields
}
I have form with field type entity on this field I have query_builder which return query and 'property' => 'name',. And my question what need to do in data transformer for change select name, need complicated with several filed, example - name_address_office. Using Symfony 2.8 I need dataTransformer approach
my form
class OutBoundInvoiceRowType extends AbstractType
{
/**
* #var array
*/
private $vatClasses;
/**
* #var Container
*/
private $container;
/**
* #var EntityManager
*/
private $em;
/**
* OutBoundInvoiceRowType constructor.
* #param Container $container
* #param $vatClasses
*/
public function __construct(
Container $container,
$vatClasses
) {
$this->container = $container;
$this->vatClasses = $vatClasses;
$this->em = $this->container->get('doctrine.orm.entity_manager');
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('location', 'entity', array(
'class' => Location::class,
'property' => 'name',
'empty_value' => 'Choice Location',
'query_builder' => self::getLocations(),
'required' => false
))
->add('vat', ChoiceType::class, [
'choices' => ?,
'required' => true,
])
$builder->get('vat')
->addModelTransformer(new VatTransformer($this->container));
}
and my VatTransformer:
class VatTransformer implements DataTransformerInterface
{
/**
* #var Container
*/
private $container;
/**
* #var EntityManager
*/
private $em;
/**
* LocationTransformer constructor.
* #param Container $container
*/
public function __construct(Container $container)
{
$this->container = $container;
$this->em = $this->container->get('doctrine.orm.entity_manager');
}
/**
*
* #param Location|null $issue
* #return string
*/
public function transform($issue)
{
if (null === $issue) {
return '';
}
}
}
in function transform $issue have null and when return '' nothing change in form, still have 'property' => 'name', on choice, What need to do in data transform name ?
this now I have
and this what I need
need name of several parts
UPDATE
Ok. I have choice field vat and I need build data in choice for vat field like - from some entity field, example entity Location (id, name)
How this realized with dataTransformer ?
Implement a __toString() method into your entity, which will return the desired name. Read more on the docs.
Then you remove the property from the QueryBuilder, this will cause the automatic usage of the to_string() method of the class.
I have an issue on my project using Symfony2. Is a 'worklog' project, with tickets(issues) for a specific project. But when I try to edit a worklog entry I have this error :
Catchable Fatal Error: Object of class
Proxies__CG__\AppBundle\Entity\Ticket could not be converted to
string
This is my db model :
And this is a part of the code from AppBundle/Entity/Worklog/
/**
* #var \Ticket
*
* #ORM\ManyToOne(targetEntity="Ticket")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ticket_ticket_id", referencedColumnName="ticket_id")
* })
*/
private $ticketTicket;
And from AppBundle/Entity/Ticket/
/**
* #var integer
*
* #ORM\Column(name="ticket_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $ticketId;
Do you have any idea why I have this errors ? Any idea to help ?
Worklog Form:
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('worklogDate')
->add('worklogDuration')
->add('worklogDescription')
->add('ticketTicket')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Worklog'
));
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_worklog';
}
The problem is your entity field not expected return.
try add:
public function __toString()
{
return (string) $this->getTicket();
}
Why don't declare your fields fully in your Form class. It would be smth like this:
->add('ticketTicket', 'entity', array(
'class' => 'AppBundle\Entity\Ticket',
'property' => 'propertyName', //needed property's name
'label' => 'choice_field_label'
))
If you need smth more complicated then just findAll for this field, you could use query_builder option:
->add('ticketTicket', 'entity', array(
'class' => 'AppBundle\Entity\Ticket',
'property' => 'propertyName', //needed property's name
'label' => 'choice_field_label',
'query_builder' => function(EntityRepository $er) {
return $er->findAllTicketNames();
//Where findAllTicketNames is the name of method in your
// ticketRepo which returns queryBuilder,
//instead of this you could just write your custom query like
//$qb = $er->createQueryBuilder('t');
//$qb->andWhere(...);
//return $qb;
}
))
p.s.
my answer was copied from my previous answer and some little modifications were added to suit your case :)