Compiler pass - resolve target entity doesn't load - doctrine-orm

I'm working on a bundle and i need to load a doctrine resolve_target_entities from a configuration parameter.
This article should be my solution, the fact is that using the bundle it seems not to load the "compiler pass class".
This is my bundle class
class PersonalBundle extends Bundle
{
public function build(ContainerBuilder $container){
parent::build($container);
$container->addCompilerPass(new ResolveTargetEntitiesPass());
}
}
This is the ResolveTargetEntitiesPass class
class ResolveTargetEntitiesPass implements CompilerPassInterface
{
/**
* {#inheritdoc}
*/
public function process(ContainerBuilder $container)
{
// Gets the custom entity defined by the user (or the default one)
$customEntityClass = $container->getParameter('personal.custom_class');
// Skip the resolve_target_entities part if user has not defined a different entity
if (DefaultClassInterface::DEFAULT_ENTITY_CLASS == $customEntityClass) {
return;
}
// Throws exception if the class isn't found
if (!class_exists($customEntityClass)) {
throw new ClassNotFoundException(sprintf("Can't find class %s ", $customEntityClass));
}
// Get the doctrine ResolveTargetEntityListener
$def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity');
// Adds the resolve_target_enitity parameter
$def->addMethodCall('addResolveTargetEntity', array(
DefaultClassInterface::DEFAULT_ENTITY_CLASS, $customEntityClass, array()
));
// This was added due this problem
// https://stackoverflow.com/a/46656413/7070573
if (version_compare(Version::VERSION, '2.5.0-DEV') < 0 && !$def->hasTag('doctrine.event_listener')) {
$def->addTag('doctrine.event_listener', array('event' => 'loadClassMetadata'));
} elseif (!$def->hasTag('doctrine.event_subscriber')) {
$def->addTag('doctrine.event_subscriber');
}
}
}
When i use the class it raises this error
Expected value of type "PersonalBundle\Entity\DefaultClass"
for association field "PersonalBundle\Entity\Group#$defaultClass", got
"App\Entity\CustomClass" instead.
As i said it seems not to load the ResolveTargetEntitiesPass...
Thanks

So i solved the problem changing the priority of the compiler pass.
I've tried to move the bundle on top in config/bundle.php and it started working, then following this https://symfony.com/blog/new-in-symfony-3-2-compiler-passes-improvements i've left the default type but increased the priority (from 0, default, to 1).
I'm not sure which service has been "downgraded" so if anyone has an idea it's welcome.
<?php
// ...
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
class PersonalBundle extends Bundle
{
public function build(ContainerBuilder $container){
parent::build($container);
$container->addCompilerPass(new ResolveTargetEntitiesPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1);
}
}

Related

Laravel 5.5 - Helper class works inside view/blade template, but doesn't work inside controllers

I've created myself a helper class called Perm that is meant to return user of current session (I know, I could use default auth/user, but that wouldn't be as much of a fun as creating one from scratch!)
..sadly, the created helper class only works inside a view, but doesn't work at all in controllers.. which kinda misses the point.
Whenever I'm trying to use it inside a controller, it pops:
"Class 'App\Http\Controllers\Perm' not found"
I would most appreciate any help.
HelperServiceProvider.php:
namespace App\Providers;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class HelperServiceProvider extends ServiceProvider
{
public function boot()
{
//
}
public function register()
{
foreach( glob (app_path().'/Helpers/*.php' ) as $filename ) // register all helpers
{
require_once($filename);
}
}
}
Helpers/PermHelper.php:
use App\User;
class Perm
{
public static function user()
{
if(!session('user_id')) return null;
return User::find(session('user_id'));
}
}
Portion of config/app.php, the 'providers' array:
// Custom
App\Providers\HelperServiceProvider::class,
If you are having this issue aswell.
Use proper namespacing.

Embed a Collection of Forms Error: Could not determine access type for property

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.

symfony2 phpunit session doesn't start

I have a strange problem with $_SESSION in my application
For different reasons that i don't explain here i had the necessity to set in the session the environment in my AppKernel.php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;
class AppKernel extends Kernel
{
protected $session;
public function initializeContainer()
{
parent::initializeContainer();
$this->session = $this->container->get('session');
$this->session->set('isTestEnv', $this->getEnvironment() == 'test');
}
In another class that i use to manage the request to some API i need now to get that param
namespace Bioversity\ServerConnectionBundle\Repository;
use Symfony\Component\HttpFoundation\Session\Session;
use Bioversity\ServerConnectionBundle\Repository\ServerResponseManager;
use Bioversity\ServerConnectionBundle\Repository\ServerResponseRequestQueryManager;
class ServerRequestManager
{
protected $wrapper= "http://url/to/the/api.php";
public function __construct()
{
if($_SESSION['_sf2_attributes']['isTestEnv'] == 'test')
{
$this->wrapper= "http://url/to/the/api.test.php";
}
}
Everything works fine in the browser, but when i try to run the test i get a strange error
48) ServerConnectionBundle\Tests\Repository\TraitConnectionRepositoryTest::testGetTags
ErrorException: Notice: Undefined variable: _SESSION in ServerConnectionBundle/Repository/ServerRequestManager.php line 41
---------------EDIT------------
I had updated my code,
now i have a services like this
parameters:
env: %kernel.environment%
services:
bioversity_server_connection:
class: Bioversity\ServerConnectionBundle\Repository\ServerRequestManager
arguments: [%env%]
and in my class i added
class ServerRequestManager
{
public function __construct($env)
{
print_r($env);
//if(array_key_exists('isTestEnv', $_SESSION['_sf2_attributes']))
//{
//if($_SESSION['_sf2_attributes']['isTestEnv'] == 'test'){
if($env == 'test')
{
$this->wrapper= "http://temp.wrapper.grinfo.net/TIP/Wrapper.test.php";
$this->setDatabaseOntology('TEST-'.$this->getDatabaseOntology());
$this->setDatabasePGRSecure('TEST-'.$this->getDatabasePGRSecure());
$this->setDatabaseUsers('TEST-'.$this->getDatabaseUsers());
}
//}
}
I had imported the services.yml in my config.yml
but I only have this error
Warning: Missing argument 1 for Bioversity\ServerConnectionBundle\Repository\ServerRequestManager::__construct(), called in /home/aczepod/Sites/Bioversity/src/Bioversity/SecurityBundle/Repository/ServerConnection.php
What is wrong now!?
Woha, what the heck are you doing. Symfony has the parameter kernel.environment which refers to the current environment! So simple inject %kernel.environment% as argument into your service.
Btw. with PHP-CLI (running tests) there is no $_SESSION at all.

symfony 1.4 reuse variable declared in component.class.php [duplicate]

I want to make some action (php script) before all actions in my frontend app and then pass a result from that script to actions in variable - so I can get variable value from all actions. Where should I declare sth like this?
If the filter solution dont feet your needs, you can also create a base action class with a preExecute function:
// app/frontend/lib/baseActions.class.php
class baseActions extends sfActions
{
public function preExecute()
{
$this->myVar = .... // define your vars...
}
}
Then your module actions class extends your baseActions class:
// app/frontend/modules/myModule/actions/actions.class.php
class myModuleActions extends baseActions
{
public function executeIndex(sfWebRequest $request)
{
// var $this->myVar is available in any action and in your template
...
}
}
if you have to use the preExecute function in your module class action, remember to call parent::preExecute() in it.
What kind of information ?
I would recommend you to use filters.
In your apps/frontend/config/filters.yml:
rendering: ~
myfilter:
class: myCustomFilter
Create the file lib/filter/myCustomFilter.php:
<?php
class myCustomFilter extends sfFilter
{
public function execute ($filterChain)
{
if ($this->isFirstCall())
{
// do what ever you want here.
$config = Doctrine_Core::getTable('Config')->findAll();
sfConfig::set('my_config', $config);
}
$filterChain->execute();
}
}
And then, every where, you can retrieve your data:
sfConfig::get('my_config');

How add BLOB type in Doctrine 2 using Symfony 2

In Symfony 2 I generate a Bundle for storing any type of document into database, but I need the BLOB column type.
Tnx to this question I add the class BlobType into Doctrine DBAL, but for use the new column type I had to change
Doctrine\DBAL\Types\Type
[...]
const BLOB = 'blob';
[...]
private static $_typesMap = array(
[...],
self::BLOB => 'Doctrine\DBAL\Types\BlobType',
);
Doctrine\DBAL\Platforms\MySqlPlatform (maybe it was better if I had changed Doctrine\DBAL\Platforms\AbstractPlatform)
[...]
protected function initializeDoctrineTypeMappings()
{
$this->doctrineTypeMapping = array(
[...],
'blob' => 'blob',
);
}
[...]
/**
* Obtain DBMS specific SQL to be used to create time fields in statements
* like CREATE TABLE.
*
* #param array $fieldDeclaration
* #return string
*/
public function getBlobTypeDeclarationSQL(array $fieldDeclaration)
{
return 'BLOB';
}
Now I don't have mouch time for a 'pretty solution', but in future I would like to restore the Doctrine classes and be able to assign the new column type into Symfony 2 bootstrap.
I think I should edit my app/bootstrap.php.cache but I don't have idea how to intervene.
this worked for me:
create your blobtype (See https://gist.github.com/525030/38a0dd6a70e58f39e964ec53c746457dd37a5f58)
add this to your Bundle initialization (/src/YOURDOMAIN/YOURBUNDLE/YOURDOMAINYOUBUNDLE.php)
class YourBundle extends Bundle
{
public function boot()
{
$em = $this->container->get('doctrine.orm.entity_manager');
Type::addType('blob', 'YOURDOMAIN\YOURBUNDLE\YOURTYPEDIRECTORY\BlobType');
$em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('blob','blob');
}
}
Small improvement for registration blob type in XXXBundle::boot(), but can be necessary during unittests.
class XXXBundle extends Bundle
{
public function boot()
{
// Add blob type
if(!Type::hasType('blob')) {
Type::addType('blob', '{CLASS_PATH}\\Blob');
}
// Add blob type to current connection.
// Notice: during tests there can be multiple connections to db so
// it will be needed to add 'blob' to all new connections if not defined.
$em = $this->container->get('doctrine.orm.entity_manager');
if (!$em->getConnection()->getDatabasePlatform()->hasDoctrineTypeMappingFor('blob')) {
$em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('blob','blob');
}
}
I just found this gist:
https://gist.github.com/525030/38a0dd6a70e58f39e964ec53c746457dd37a5f58
app/bootstrap.php:
<?php
// ...
$em = Doctrine\ORM\EntityManager::create($conn, $config, $evm);
// types registration
Doctrine\DBAL\Types\Type::addType('blob', 'Doctrine\DBAL\Types\Blob');
$em->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('BLOB', 'blob');
BTW bootstrap.cache.php is auto-generated AFAIK.. So changes there would be overwritten.