I am trying to embed collection of Tag forms to Service form, according to this tutorial. Tag and Service entities have many-to-many relationship.
Form is rendering correctly. But when I submit form, I get
Could not determine access type for property "tagList"
error. I don't understand why new Tag object is not added to the Service class by calling the addTag() method.
ServiceType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class, array(
'label' => 'Title'
))
;
$builder->add('tagList', CollectionType::class, array(
'entry_type' => TagType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false
)));
}
Service class
{
....
/**
* #ORM\ManyToMany(targetEntity="Tag", mappedBy="serviceList",cascade={"persist"})
*/
private $tagList;
/**
* #return ArrayCollection
*/
public function getTagList()
{
return $this->tagList;
}
/**
* #param Tag $tag
* #return Service
*/
public function addTag(Tag $tag)
{
if ($this->tagList->contains($tag) == false) {
$this->tagList->add($tag);
$tag->addService($this);
}
}
/**
* #param Tag $tag
* #return Service
*/
public function removeTag(Tag $tag)
{
if ($this->tagList->contains($tag)) {
$this->tagList->removeElement($tag);
$tag->removeService($this);
}
return $this;
}
}
Tag class
{
/**
* #ORM\ManyToMany(targetEntity="Service", inversedBy="tagList")
* #ORM\JoinTable(name="tags_services")
*/
private $serviceList;
/**
* #param Service $service
* #return Tag
*/
public function addService(Service $service)
{
if ($this->serviceList->contains($service) == false) {
$this->serviceList->add($service);
$service->addTag($this);
}
return $this;
}
/**
* #param Service $service
* #return Tag
*/
public function removeService(Service $service)
{
if ($this->serviceList->contains($service)) {
$this->serviceList->removeElement($service);
$service->removeTag($this);
}
return $this;
}
}
ServiceController
public function newAction(Request $request)
{
$service = new Service();
$form = $this->createForm('AppBundle\Form\ServiceType', $service);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($service);
$em->flush();
return $this->redirectToRoute('service_show', array('id' => $service->getId()));
}
return $this->render('AppBundle:Service:new.html.twig', array(
'service' => $service,
'form' => $form->createView(),
));
}
Could you please try to implement code from this URL?
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#owning-and-inverse-side-on-a-manytomany-association
First, please try to change mapped/inverse sides, and remove $service->addTag($this); from Tag::addService method.
Short version:
I just ran into this problem and solved it by adding a setter for the affected property:
Could not determine access type for property "tagList"
public function setTagList(Array $tagList)
{
$this->tagList = $tagList;
}
Long version:
The error message is signaling that Symfony is trying to modify the object's state, but cannot figure out how to actually make the change due to the way its class is set up.
Taking a look at Symfony's internals, we can see that Symfony gives you 5 chances to give it access and picks the best one in this order from top to bottom:
A setter method named setProperty() with one argument:
This is the first thing Symfony checks for and is the most explicit way to achieve this. As far as I'm aware this is the best practice:
class Entity {
protected $tagList;
//...
public function getTagList()
{
return $this->tagList;
}
//...
}
A combined getter and setter in one method with one argument:
It's important to realize that this method will also be accessed by Symfony in order to get the object's state. Since those method calls don't include an argument, the argument in this method must be optional.
class Entity {
protected $tagList;
//...
public function tagList($tags = null)
{
if($reps){
$this->tagList = $tags;
} else {
return $this->tagList;
}
}
//...
}
The affected property being declared as public:
class Entity {
public $tagList;
//... other properties here
}
A __set magic method:
This will affect all properties rather than just the one you intended.
class Entity {
public $tagList;
//...
public function __set($name, $value){
$this->$name = $value;
}
//...
}
A __call magic method (in some cases):
I wasn't able to confirm this, but the internal code suggests this is possible when magic is enabled on PropertyAccessor's construction.
Only using one of the above strategies is required.
Maybe the problem is that Symfony can't access that property?
If you look at where that exception is thrown (writeProperty method in the PropertyAccessor class) it says it can be thrown:
If the property does not exist or is not public.
In the tutorial you mentioned it has property $tags, and method addTag. I'm just guessing here, but maybe there's a convention where it tries to call a method names add($singularForm) and this is failing for you because the property is tagList and the method is addTag.
I'm not 100% sure, but you could try debugging by setting a stop point in that Symfony method to see why it's being thrown.
Maybe you forgot in the __construct() of Service class and Tag class to initialize $tagList and $serviceList like this ?
$this->tagList = new ArrayCollection();
$this->serviceList = new ArrayCollection();
This seems like an error with your constructor. Try this :
public function __construct()
{
$this-> tagList = new \Doctrine\Common\Collections\ArrayCollection();
}
It's a long shot, but looking at your annotations I think the problem might be related to your manyToMany relationship. Try to change the owning side and inverse side (Swap the relationship) unless you specifically need to update from both ends (In that case I think the only solution is to add the objects manually or use oneToMany relationships).
Changes made only to the inverse side of an association are ignored.
Make sure to update both sides of a bidirectional association (or at
least the owning side, from Doctrine’s point of view)
This is a problem related to Doctrine I have suffered before, see:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/unitofwork-associations.html
Based on Symfony 3.3.10
I actually faced this problem many and many times, finally once i discovered where this problem was coming from, depending on the name you give to your entity property it can happen that the adder and the remover for your collection property aren't exactly what you are expecting.
Example: Your entity properity name is "foo" and you would expect the adder to be called "addFoo" and remover "removeFoo", but then all of a sudden the "Could not determine access type for property" appear.
So you start going into fear searching for w/e problems in your code, instead you just have to look this file inside Symfony core files:
vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
Inside this file there's a method called findAdderAndRemover.
Go there with your debugger and you will eventually find out that symfony searches for weird name for your adder/remover, they may actually end with "um" or "on" or "us" depending on the language (human language) you used to name them. Since i'm Italian this happen quite often.
Watch out for that, since the fix may be as simple as changing the name used for your add/remove method inside your entity to make them match with what Symfony core is looking for.
This happens to me when i use bin/console doctrine:generate:entities to create the methods automatically for me
If you are using symfony, and use EntityRepository instead of CollectionType, make sure you use the 'multiple' => true, on your form build, otherwise the input will be for one entity and not for many, therefore it will call the setTagList instead of using the methods addTagList and removeTagList.
I have problem unit testing method inside closure called by call_user_func() example :
public function trans($lang, $callback)
{
$this->sitepress->switch_lang($lang);
call_user_func($callback);
}
on controller :
public function sendMail()
{
$foo = $baz = 'something';
$mail = $this->mailer;
$this->helper->trans_c('en', function() use($foo, $baz, $mail) {
$mail->send('Subject', $foo, $baz);
});
}
test case :
public function testSomething()
{
$helperMock = Mockery::mock('Acme\Helper');
$helperMock->shouldReceive('trans_c')->once(); // passed
$mailMock = Mockery::mock('Acme\Mail');
$mailMock->shouldReceive('send')->once(); // got should be called 1 times instead 0
$act = new SendMailController($helperMock, $mailMock);
$act->sendMail();
}
how can I ensure that ->send() method is called inside closure trans_c()
I tried with
$helperMock->shouldReceive('trans_c')->with('en', function() use($mailMock) {
$mailMock->shouldReceive('send');
});
no luck. :(
well it works fine with passing Mockery::type('Closure') in the second param of trans_c, but I really need to ensure that method send from mailer class is called.
A mocked class does not execute the real code by default. If you mock the helper it will check that the calls are being made but won't execute the anonymous function.
With mockery, you can configure the expectation so that the real method will be executed: passthru();
Try this:
$helperMock = Mockery::mock('Acme\Helper');
$helperMock
->shouldReceive('trans_c')
->once()
->passthru()
;
This is explained in the docs.
EDIT
Maybe you don't really need to mock the helper. If you mock the Mail class and expect the send method to be called once, just let the real helper do it.
I have a question about routing while testing packages. The function setRoutes creates new routes in the test file as follows:
class PackageTests extends \Orchestra\Testbench\TestCase {
protected function setRoutes()
{
Route::group([
'prefix' => Package::functionToCall1(),
'before' => 'filter'
], function() {
Route::get('/', function () {
return "hello";
});
});
Route::enableFilters();
}
protected function getEnvironmentSetUp($app)
{
$this->app = $app;
$this->setRoutes();
Config::set('app.url', "http://localhost/" );
}
public function testFunction1()
{
$crawler = $this->call(
'GET',
'http://localhost/'
);
// doing this call, the function on the prefix is called
$this->assertResponseOk();
}
}
Inside the function called in the prefix, functionToCall1() urls are not taken successfully. A call to URL::current() returns "/" and a call to Request::fullUrl() returns "http://:" when phpunit is executed but they returns the full url when used executing a url in the browser. This is the code of the package:
class Package
{
function functionToCall1()
{
var_dump(URL::current() ); // returns "/"
var_dump(Request::fullUrl()); // returns "http://:"
// I want them to return 'http://localhost'
}
}
I tried setting up the url Config::set('app.url', "http://localhost/" ); but it was useless.
To sum up, is there a way to call a function in the prefix and get the testing url?
Thanks, I would really appreciate your answers :)
I have had to deal with a similar issue. My solution was found here:
Mocking Laravel's Request::segment method
Apparently there is an order of operations issue with testing a Request facade.
I was trying to use Request::segments() before the request was being built, so there were never any segments to return.
I imagine it's the same problem with Request::fullUrl().
Here is my solution:
class MyTestClass extends TestCase
{
public function setUp()
{
// No call to parent::setUp()
$this->app = $this->createApplication();
$this->app->request->server->set('REQUEST_URI', '/some/uri');
$this->client = $this->createClient();
$this->app->boot();
}
public function testWhatever()
{
$this->call('GET', '/some/uri');
}
}
This allows me to get the request data properly, even though it looks pretty bad.
PHPUnit has setup and tearDown events that run, respectively, before and after each test within a test case. In my specific scenario, I also want to run something like a testCaseSetup and testCaseTearDown. Is that possible?
Current solution looks like this:
<?php
class MyTestCase extends \PHPUnit_Framework_TestCase
{
public function __construct($name = NULL, array $data = array(), $dataName = '')
{
// My test case setup logic
parent::__construct($name, $data, $dataName);
}
public function __destruct()
{
// My test case tear down logic
}
}
But it seems far from optimal for the following reasons:
I have to redeclare PHPUnit_Framework_TestCase construct and redirect any arguments. IF PHPUnit constructor is changed on a version update, my test case will stop.
Probably PHPUnit_Framework_TestCase was not declared to be used like this.
I would like to know if there are better solutions. Any ideas?
Yes, there are special methods for that purpose: setUpBeforeClass and tearDownAfterClass.
class TemplateMethodsTest extends PHPUnit_Framework_TestCase
{
public static function setUpBeforeClass()
{
// do sth before the first test
}
public static function tearDownAfterClass()
{
// do sth after the last test
}
I have the following test:
[Test]
public void VerifyThat_WhenInitializingTheLoggingInterceptionFacility_TheLoggingInterceptorIsAdded()
{
var kernel = new Mock<IKernel>(MockBehavior.Loose)
{
DefaultValue = DefaultValue.Mock
};
kernel.Setup(k => k.AddFacility<LoggingInterceptionFacility>())
.Returns(kernel.Object)
.Callback(() => ((IFacility)new LoggingInterceptionFacility()).Init(kernel.Object, Mock.Of<IConfiguration>()));
kernel.Setup(k => k.Register(It.IsAny<IRegistration[]>()))
.Returns(kernel.Object)
.Verifiable();
kernel.Object.AddFacility<LoggingInterceptionFacility>();
kernel.Verify(k => k.Register(It.Is<IRegistration[]>(r => r.Contains(Component.For<LoggingInterceptor>()))));
}
As you can see I am mocking the real behavior of the kernel by calling the facilitiy's Init(IKernel, IConfiguration) method which in turns calls the protected Init() method.
Here's how the protected Init() looks like:
protected override void Init()
{
Kernel.ProxyFactory.AddInterceptorSelector(new LoggingModelInterceptorsSelector());
Kernel.Register(Component.For<LoggingInterceptor>());
}
I expected that the verification would pass but it does not. If I verify that the Kernel.Register was called at all with It.IsAny<LoggingInterceptor>() the test passes.
What am I not matching right here? Is there a way to make this test pass?
It seems like you are testing way too much here. You are effectively reimplmenting a lot of Windsor's internals by piping calls from AddFacility to LoggingInterceptionFacility.Init.
All you really need to test is the fact that your facility calls Register on the kernel and assume that Windsor does the right thing. After all, it has unit tests of its own ;)
After doing that, the test becomes much more readable, which I consider the most important aspect.
[Test]
public void VerifyThat_WhenInitializingTheLoggingInterceptionFacility_TheLoggingInterceptorIsAdded()
{
var kernel = new Mock<IKernel>();
kernel.Setup(k => k.Register(It.IsAny<IRegistration[]>()))
.Returns(kernel.Object)
.Verifiable();
//Explicit interface implementation requires casting to the interface
((IFacility)new LoggingInterceptionFacility()).Init(kernel.Object, Mock.Of<IConfiguration>().Object);
//verify the type of registration here
kernel.Verify(k => k.Register(It.Is<IRegistration[]>(r => r[0] is ComponentRegistration<LoggingInterceptor>);
}
EDIT Calls to Component.For return different instances between setup and execution. I updated the code to reflect that and have the verification check the type of the component.